#
-# Copyright 2011 Canonical Ltd.
+# Copyright 2011-2012 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
@@ -18,13 +16,18 @@
"""Tests for the Control Panel."""
+import copy
import logging
import operator
import os
+import Queue
+import shutil
+from PyQt4 import QtGui
from twisted.internet import defer
from ubuntuone.devtools.handlers import MementoHandler
+from ubuntuone.controlpanel.tests import helper_fail
from ubuntuone.controlpanel.gui.tests import (
FAKE_VOLUMES_INFO,
FAKE_VOLUMES_MINIMAL_INFO,
@@ -33,18 +36,25 @@
)
from ubuntuone.controlpanel.gui.qt import folders as gui
from ubuntuone.controlpanel.gui.qt.tests import (
+ BaseTestCase,
FakedDialog,
)
from ubuntuone.controlpanel.gui.qt.tests.test_ubuntuonebin import (
UbuntuOneBinTestCase,
)
-
# Access to a protected member
# Instance of 'ControlBackend' has no '_called' member
# pylint: disable=W0212, E1103
+def volumes_with_music_unsubscribed():
+ """Return a copy of FAKE_VOLUMES_MINIMAL_INFO with music unsubscribed."""
+ volumes = copy.deepcopy(FAKE_VOLUMES_MINIMAL_INFO)
+ volumes[0][2][1][u'subscribed'] = u''
+ return volumes
+
+
def _build_name(name):
"""Helper to build the name expected when showing folder info."""
if name:
@@ -54,6 +64,27 @@
return name
+class ExploreFolderButtonTestCase(BaseTestCase):
+ """Test the ExploreFolderButton widget."""
+
+ class_ui = gui.ExploreFolderButton
+ kwargs = dict(folder_path=u'foo')
+
+ def test_text(self):
+ """The button text is correct."""
+ self.assertEqual(unicode(self.ui.text()),
+ gui.FOLDERS_COLUMN_EXPLORE)
+
+ def test_clicked(self):
+ """Clicking the button opens the folder in the default file manager."""
+ self.patch(gui, 'uri_hook', self._set_called)
+ self.ui.click()
+
+ url = gui.QtCore.QUrl.fromLocalFile(self.kwargs['folder_path'])
+ expected = unicode(url.toString())
+ self.assertEqual(self._called, ((expected,), {}))
+
+
class FoldersPanelTestCase(UbuntuOneBinTestCase):
"""Test the qt cloud folders tab."""
@@ -71,9 +102,15 @@
self.memento.setLevel(logging.DEBUG)
gui.logger.addHandler(self.memento)
- old_home = os.environ['HOME']
- os.environ['HOME'] = USER_HOME
- self.addCleanup(lambda: os.environ.__setitem__('HOME', old_home))
+ def set_item_checked(self, item, checked=True):
+ """Make item to be checked."""
+ checkbox = self.ui.ui.folders.itemWidget(item, gui.SUBSCRIPTION_COL)
+ checkbox.setCheckState(gui.CHECKED if checked else gui.UNCHECKED)
+
+ def get_item_checked(self, item):
+ """Get if item is checked."""
+ checkbox = self.ui.ui.folders.itemWidget(item, gui.SUBSCRIPTION_COL)
+ return (checkbox.checkState() == gui.CHECKED)
class FoldersPanelVolumesInfoTestCase(FoldersPanelTestCase):
@@ -86,10 +123,48 @@
def assert_folder_group_header_correct(self, item, name):
"""Check that the folder group header is correct."""
- self.assertEqual(item.text(gui.FOLDER_NAME_COL), name)
- self.assertEqual(item.text(gui.SUBSCRIPTION_COL),
+ self.assertEqual(unicode(item.text(gui.FOLDER_NAME_COL)), name)
+ self.assertEqual(unicode(item.text(gui.SUBSCRIPTION_COL)),
gui.FOLDERS_COLUMN_SYNC_LOCALLY)
- self.assertEqual(item.text(gui.EXPLORE_COL), '')
+ if not self.ui.remote_folders:
+ self.assertEqual(unicode(item.text(gui.EXPLORE_COL)), '')
+
+ def assert_folder_row_correct(self, item, label, icon_name, volume,
+ tweaked_path=None):
+ """Check that the folder row 'item' is correct."""
+ folders = self.ui.ui.folders
+
+ actual_label = unicode(item.text(gui.FOLDER_NAME_COL))
+ self.assertEqual(label, actual_label)
+
+ if volume['type'] == self.ui.backend.ROOT_TYPE:
+ # no check box but the ALWAYS_SUBSCRIBED legend
+ self.assertEqual(unicode(item.text(gui.SUBSCRIPTION_COL)),
+ gui.ALWAYS_SUBSCRIBED)
+ else:
+ subscribed = self.get_item_checked(item)
+ self.assertEqual(subscribed, bool(volume['subscribed']))
+
+ actual_icon_name = item.icon_obj.icon_name
+ self.assertEqual(icon_name, actual_icon_name)
+
+ self.assertEqual(item.volume_id, volume['volume_id'])
+
+ expected_path = volume['path']
+ if tweaked_path is not None:
+ expected_path = tweaked_path
+ self.assertEqual(item.volume_path, expected_path)
+
+ # tooltips are correct
+ self.assertEqual(item.toolTip(gui.FOLDER_NAME_COL), label)
+ self.assertEqual(item.toolTip(gui.EXPLORE_COL),
+ gui.FOLDERS_COLUMN_EXPLORE)
+
+ if not self.ui.remote_folders:
+ # explore button is in place
+ model_index = folders.indexFromItem(item, gui.EXPLORE_COL)
+ button = folders.indexWidget(model_index)
+ self.assertEqual(button.isEnabled(), bool(volume['subscribed']))
@defer.inlineCallbacks
def test_is_processing_while_asking_info(self):
@@ -131,7 +206,6 @@
self.assert_folder_group_header_correct(item, name)
- # check children
self.assertEqual(len(volumes), item.childCount())
sorted_vols = sorted(volumes, key=operator.itemgetter('path'))
for volume in sorted_vols:
@@ -144,38 +218,14 @@
if volume['type'] == self.ui.backend.SHARE_TYPE:
name = volume['name']
expected_path = volume['realpath']
- label = item.text(gui.FOLDER_NAME_COL)
- self.assertEqual(label, name)
- if volume['type'] == self.ui.backend.ROOT_TYPE:
- # no check box but the ALWAYS_SUBSCRIBED legend
- self.assertEqual(item.text(gui.SUBSCRIPTION_COL),
- gui.ALWAYS_SUBSCRIBED)
- else:
- subscribed = item.checkState(gui.SUBSCRIPTION_COL) == \
- gui.CHECKED
- self.assertEqual(subscribed, bool(volume['subscribed']))
-
- icon_name = item.icon_obj.icon_name
if volume['type'] != self.ui.backend.SHARE_TYPE:
- self.assertEqual(icon_name, gui.FOLDER_ICON_NAME)
+ icon_name = gui.FOLDER_ICON_NAME
else:
- self.assertEqual(icon_name, gui.SHARE_ICON_NAME)
-
- self.assertEqual(item.volume_id, volume['volume_id'])
- self.assertEqual(item.volume_path, expected_path)
+ icon_name = gui.SHARE_ICON_NAME
- # tooltips are correct
- self.assertEqual(item.toolTip(gui.FOLDER_NAME_COL), name)
- self.assertEqual(item.toolTip(gui.EXPLORE_COL),
- gui.FOLDERS_COLUMN_EXPLORE)
-
- # explore button is in place
- model_index = folders.indexFromItem(item, gui.EXPLORE_COL)
- button = folders.indexWidget(model_index)
- self.assertEqual(button.isFlat(), True)
- self.assertEqual(button.isEnabled(),
- bool(volume['subscribed']))
+ self.assert_folder_row_correct(item, name, icon_name, volume,
+ tweaked_path=expected_path)
treeiter += 1
item = treeiter.value()
@@ -284,9 +334,12 @@
self.assertTrue(self.memento.check_warning(path, 'does not exist'))
self.assertEqual(self._called, False)
- def test_process_info_with_music_folder(self):
+ def test_process_info_with_music_folder(self, volumes=None):
"""The volumes info is processed when ready."""
- self.ui.process_info(FAKE_VOLUMES_MINIMAL_INFO)
+ if volumes is None:
+ volumes = FAKE_VOLUMES_MINIMAL_INFO
+
+ self.ui.process_info(volumes)
folders = self.ui.ui.folders
treeiter = gui.QtGui.QTreeWidgetItemIterator(folders)
@@ -298,25 +351,79 @@
treeiter += 1
item = treeiter.value()
- volume = MUSIC_FOLDER
+ volume = volumes[0][2][1]
+
+ self.assert_folder_row_correct(item, gui.MUSIC_DISPLAY_NAME,
+ gui.MUSIC_ICON_NAME, volume)
- label = item.text(gui.FOLDER_NAME_COL)
- self.assertEqual(label, gui.MUSIC_DISPLAY_NAME)
+ def test_focus_order(self):
+ """Ensure that the inner widgets are in the correct tab order."""
+ self.ui.process_info(FAKE_VOLUMES_INFO)
+ folders = self.ui.ui.folders
- subscribed = item.checkState(gui.SUBSCRIPTION_COL) == gui.CHECKED
- self.assertEqual(subscribed, bool(volume['subscribed']))
+ widget = self.ui.ui.folders.nextInFocusChain()
+ treeiter = gui.QtGui.QTreeWidgetItemIterator(folders)
+ for name, _, volumes in FAKE_VOLUMES_INFO:
+ item = treeiter.value()
+ sorted_vols = sorted(volumes, key=operator.itemgetter('path'))
+ for volume in sorted_vols:
+ treeiter += 1
+ item = treeiter.value() # get child folder
- icon_name = item.icon_obj.icon_name
- self.assertEqual(icon_name, gui.MUSIC_ICON_NAME)
+ name = volume['path'].replace(USER_HOME + os.path.sep, '')
+ if volume['type'] == self.ui.backend.SHARE_TYPE:
+ name = volume['name']
+ self.assertEqual(unicode(item.text(gui.FOLDER_NAME_COL)), name)
- self.assertEqual(item.volume_id, volume['volume_id'])
- self.assertEqual(item.volume_path, volume['path'])
+ if volume['type'] != self.ui.backend.ROOT_TYPE:
+ self.assertIsInstance(widget, QtGui.QCheckBox)
+ self.assertEqual(unicode(
+ self.ui.widget_items[widget].text(0)), name)
+ widget = widget.nextInFocusChain()
+
+ if not self.ui.remote_folders:
+ self.assertIsInstance(widget, QtGui.QPushButton)
+ self.assertEqual(unicode(
+ self.ui.widget_items[widget].text(0)), name)
+ widget = widget.nextInFocusChain()
+
+ treeiter += 1
+ item = treeiter.value()
+
+ def test_widget_dict(self):
+ """Ensure the widget_items dictionary is full."""
+ self.ui.process_info(FAKE_VOLUMES_INFO)
+ it = QtGui.QTreeWidgetItemIterator(self.ui.ui.folders)
+ while it.value():
+ item = it.value()
+ checkbox = self.ui.ui.folders.itemWidget(item,
+ gui.SUBSCRIPTION_COL)
+ button = self.ui.ui.folders.itemWidget(item,
+ gui.EXPLORE_COL)
+ if checkbox:
+ self.assertEqual(self.ui.widget_items[checkbox],
+ item)
+ if button:
+ self.assertEqual(self.ui.widget_items[button],
+ item)
+ it += 1
def test_share_publish_button(self):
"""When clicking the share/publish button, the proper url is opened."""
+ self.assertTrue(self.ui.ui.share_publish_button.isVisible())
self.assert_uri_hook_called(self.ui.ui.share_publish_button,
gui.MANAGE_FILES_LINK)
+ def test_add_folder_button(self):
+ """The 'add_folder_button' is visible by default."""
+ self.assertEqual(self.ui.ui.add_folder_button.add_folder_func,
+ self.ui.backend.create_folder)
+ self.assertTrue(self.ui.ui.add_folder_button.isVisible())
+
+ def test_check_settings_button(self):
+ """The 'check_settings_button' is not visible by default."""
+ self.assertFalse(self.ui.ui.check_settings_button.isVisible())
+
class FoldersPanelAddFolderTestCase(FoldersPanelTestCase):
"""The test suite for the folder creation from a local dir."""
@@ -351,16 +458,25 @@
class FoldersPanelSubscriptionTestCase(FoldersPanelTestCase):
"""The test suite for the folder subscription."""
+ faked_volumes = FAKE_VOLUMES_MINIMAL_INFO
+
@defer.inlineCallbacks
def setUp(self):
yield super(FoldersPanelSubscriptionTestCase, self).setUp()
self.patch(gui.os.path, 'exists', lambda path: True)
FakedDialog.response = gui.YES
- self.ui.process_info(FAKE_VOLUMES_MINIMAL_INFO)
+ self.ui.process_info(self.faked_volumes)
# the music folder
self.item = self.ui.ui.folders.topLevelItem(0).child(1)
+ def set_item_checked(self, item=None, checked=True):
+ """Make item to be checked."""
+ if item is None:
+ item = self.item
+ test = super(FoldersPanelSubscriptionTestCase, self).set_item_checked
+ test(item=item, checked=checked)
+
@defer.inlineCallbacks
def test_on_folders_item_changed(self):
"""Clicking on 'subscribed' updates the folder subscription."""
@@ -368,10 +484,9 @@
volume = MUSIC_FOLDER
fid = volume['volume_id']
subscribed = not bool(volume['subscribed'])
- check_state = gui.CHECKED if subscribed else gui.UNCHECKED
self.ui.is_processing = True
- self.item.setCheckState(gui.SUBSCRIPTION_COL, check_state)
+ self.set_item_checked(self.item, subscribed)
self.ui.is_processing = False
yield self.ui.on_folders_itemChanged(self.item)
@@ -380,7 +495,7 @@
self.assert_backend_called('change_volume_settings',
fid, {'subscribed': subscribed})
- value = self.item.checkState(gui.SUBSCRIPTION_COL) == gui.CHECKED
+ value = self.get_item_checked(self.item)
self.assertEqual(value, bool(subscribed))
# folder list was reloaded
@@ -430,7 +545,7 @@
# make sure the item is subscribed
self.ui.is_processing = True
- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.CHECKED)
+ self.set_item_checked()
self.ui.is_processing = False
yield self.ui.on_folders_itemChanged(self.item)
@@ -449,7 +564,7 @@
# make sure the item is unsubscribed
self.ui.is_processing = True
- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.UNCHECKED)
+ self.set_item_checked()
self.ui.is_processing = False
yield self.ui.on_folders_itemChanged(self.item)
@@ -464,7 +579,7 @@
# make sure the item is subscribed
self.ui.is_processing = True
- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.CHECKED)
+ self.set_item_checked()
self.ui.is_processing = False
yield self.ui.on_folders_itemChanged(self.item)
@@ -473,8 +588,7 @@
self.assertNotIn('change_volume_settings', self.ui.backend._called)
self.assertFalse(self.ui.is_processing)
- subscribed = self.item.checkState(gui.SUBSCRIPTION_COL) == gui.CHECKED
- self.assertEqual(subscribed, False)
+ self.assertFalse(self.get_item_checked(self.item))
@defer.inlineCallbacks
def test_subscribe_does_not_call_backend_if_answer_is_no(self):
@@ -483,7 +597,7 @@
# make sure the item is subscribed
self.ui.is_processing = True
- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.CHECKED)
+ self.set_item_checked()
self.ui.is_processing = False
yield self.ui.on_folders_itemChanged(self.item)
@@ -492,15 +606,14 @@
self.assertNotIn('change_volume_settings', self.ui.backend._called)
self.assertFalse(self.ui.is_processing)
- subscribed = self.item.checkState(gui.SUBSCRIPTION_COL) == gui.CHECKED
- self.assertEqual(subscribed, False)
+ self.assertFalse(self.get_item_checked(self.item))
@defer.inlineCallbacks
def test_no_confirmation_if_unsubscribing(self):
"""The confirmation dialog is not shown if unsubscribing."""
# make sure the item is unsubscribed
self.ui.is_processing = True
- self.item.setCheckState(gui.SUBSCRIPTION_COL, gui.UNCHECKED)
+ self.set_item_checked(checked=False)
self.ui.is_processing = False
# the confirm dialog was not called so far
@@ -511,3 +624,235 @@
self.assertTrue(FakedDialog.args is None, 'dialog was not run')
self.assertTrue(FakedDialog.kwargs is None, 'dialog was hid')
+
+
+class RemoteFoldersPanelTestCase(FoldersPanelVolumesInfoTestCase):
+ """The test case for the RemoteFoldersPanel widget."""
+
+ class_ui = gui.RemoteFoldersPanel
+
+ def test_process_info_with_music_folder(self, volumes=None):
+ """The volumes info is processed when ready."""
+ volumes = volumes_with_music_unsubscribed()
+ parent = super(RemoteFoldersPanelTestCase, self)
+ parent.test_process_info_with_music_folder(volumes=volumes)
+
+ def test_share_publish_button(self):
+ """When clicking the share/publish button, the proper url is opened."""
+ self.assertFalse(self.ui.ui.share_publish_button.isVisible())
+
+ def test_add_folder_button(self):
+ """The 'add_folder_button' is not visible by default."""
+ self.assertFalse(self.ui.ui.add_folder_button.isVisible())
+
+ def test_check_settings_button(self):
+ """The 'check_settings_button' is visible by default."""
+ self.assertTrue(self.ui.ui.check_settings_button.isVisible())
+
+
+class RemoteFoldersPanelSubscriptionTestCase(FoldersPanelSubscriptionTestCase):
+ """The test suite for the remote folder subscription."""
+
+ class_ui = gui.RemoteFoldersPanel
+ faked_volumes = volumes_with_music_unsubscribed()
+
+
+class BaseLocalFoldersTestCase(BaseTestCase):
+ """Test suite for the class implementing the LocalFolders feature."""
+
+ @defer.inlineCallbacks
+ def setUp(self):
+ self.path = 'not-existing-dir'
+ self.expected_size = self.build_test_dir(self.path)
+ self.queue = Queue.Queue()
+ yield super(BaseLocalFoldersTestCase, self).setUp()
+
+ def build_test_dir(self, dir_path):
+ """Build a testing directory hierarchy."""
+ assert not os.path.exists(dir_path)
+
+ os.makedirs(dir_path)
+ self.addCleanup(shutil.rmtree, dir_path)
+
+ total_size = 0
+
+ a_file = os.path.join(dir_path, 'test_file')
+ with open(a_file, 'wb') as f:
+ f.write('z' * 1000000)
+
+ total_size += os.path.getsize(a_file)
+
+ inner_dir = os.path.join(dir_path, 'test_dir')
+ os.mkdir(inner_dir)
+
+ other_file = os.path.join(dir_path, 'other_test_file')
+ with open(other_file, 'wb') as f:
+ f.write(' ' * 99999)
+
+ total_size += os.path.getsize(other_file)
+
+ empty_dir = os.path.join(dir_path, 'empty')
+ os.mkdir(empty_dir)
+
+ # add a symlink to confirm those are avoided
+ a_link = os.path.join(dir_path, 'some_link')
+ os.symlink(a_file, a_link)
+
+ return total_size
+
+
+class CalculateSizeTestCase(BaseLocalFoldersTestCase):
+ """Test suite for the CalculateSize thread implementation."""
+
+ @defer.inlineCallbacks
+ def setUp(self):
+ yield super(CalculateSizeTestCase, self).setUp()
+ self.ui = gui.CalculateSize(path_name=self.path, queue=self.queue)
+ self.patch(self.ui, 'start', lambda: None)
+
+ def test_creation(self):
+ """The created instance is correct."""
+ self.assertEqual(self.ui.path_name, self.path)
+ self.assertEqual(self.ui.queue, self.queue)
+ self.assertTrue(self.ui.daemon)
+
+ def test_run(self):
+ """The run() method calculates the size for the given path."""
+ self.ui.run()
+
+ path, size = self.queue.get(block=True, timeout=0.5)
+
+ self.assertEqual(path, self.path)
+ self.assertEqual(size, self.expected_size)
+
+ def test_run_handles_errors(self):
+ """The run() method handles errors."""
+ self.patch(gui.os, 'walk', helper_fail)
+ self.ui.run()
+
+ self.assertRaises(Queue.Empty, self.queue.get, block=True, timeout=0.5)
+
+
+class FakedCalculateSize(object):
+ """A faked CalculateSize thread."""
+
+ def __init__(self, *args, **kwargs):
+ self.started = False
+
+ def start(self):
+ """Fake start."""
+ self.started = True
+
+
+class FolderItemTestCase(BaseLocalFoldersTestCase):
+ """Test suite for the FolderItem widget."""
+
+ @defer.inlineCallbacks
+ def setUp(self):
+ yield super(FolderItemTestCase, self).setUp()
+ self.calculator = FakedCalculateSize(self.path, self.queue)
+ self.patch(gui, 'CalculateSize', lambda *a, **kw: self.calculator)
+ self.values = ['foo', 'bar']
+
+ assert not self.calculator.started
+
+ def test_no_params(self):
+ """The creation with no params uses defaults."""
+ item = gui.FolderItem()
+
+ self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
+ self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
+ self.assertEqual(item.path, None)
+ self.assertEqual(item.volume_id, None)
+ self.assertEqual(item.thread, None)
+ self.assertEqual(item.size, 0)
+ self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
+ gui.UNCHECKED)
+
+ def test_values(self):
+ """The creation with only values."""
+ item = gui.FolderItem(values=self.values)
+
+ self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), self.values[0])
+ self.assertEqual(item.text(gui.LOCAL_SPACE_COL), self.values[1])
+ self.assertEqual(item.path, None)
+ self.assertEqual(item.volume_id, None)
+ self.assertEqual(item.thread, None)
+ self.assertEqual(item.size, 0)
+ self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
+ gui.UNCHECKED)
+
+ def test_path(self):
+ """The creation with only a path."""
+ item = gui.FolderItem(path=self.path)
+
+ self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
+ self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
+ self.assertEqual(item.path, self.path)
+ self.assertEqual(item.volume_id, None)
+ self.assertEqual(item.thread, None)
+ self.assertEqual(item.size, 0)
+ self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
+ gui.UNCHECKED)
+
+ def test_queue(self):
+ """The creation with only a queue."""
+ item = gui.FolderItem(queue=self.queue)
+
+ self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
+ self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
+ self.assertEqual(item.path, None)
+ self.assertEqual(item.volume_id, None)
+ self.assertEqual(item.thread, None)
+ self.assertEqual(item.size, 0)
+ self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
+ gui.UNCHECKED)
+
+ def test_volume_id(self):
+ """The creation with only a volume_id."""
+ item = gui.FolderItem(volume_id='yadda')
+
+ self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
+ self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
+ self.assertEqual(item.path, None)
+ self.assertEqual(item.volume_id, 'yadda')
+ self.assertEqual(item.thread, None)
+ self.assertEqual(item.size, 0)
+ self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
+ gui.UNCHECKED)
+
+ def test_path_and_volume_id(self):
+ """The creation with only a volume_id."""
+ item = gui.FolderItem(path=self.path, volume_id='yadda')
+
+ self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
+ self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
+ self.assertEqual(item.path, self.path)
+ self.assertEqual(item.volume_id, 'yadda')
+ self.assertEqual(item.thread, None)
+ self.assertEqual(item.size, 0)
+ self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
+ gui.CHECKED)
+
+ def test_path_and_queue(self):
+ """The creation with only a volume_id."""
+ item = gui.FolderItem(path=self.path, queue=self.queue)
+
+ self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
+ self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
+ self.assertEqual(item.path, self.path)
+ self.assertEqual(item.volume_id, None)
+ self.assertEqual(item.thread, self.calculator)
+ self.assertEqual(item.size, None)
+ self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
+ gui.UNCHECKED)
+
+ self.assertTrue(self.calculator.started)
+
+
+class LocalFoldersPanelTestCase(UbuntuOneBinTestCase):
+ """Test suite for the LocalFoldersPanel widget."""
+
+ class_ui = gui.LocalFoldersPanel
+
+ # TODO: add the test suite (LP: #959690).
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/gui/qt/tests/test_gotoweb.py 2012-03-21 14:06:28.000000000 +0000
@@ -18,8 +18,6 @@
"""Tests for the GoToWebButton widget."""
-from twisted.internet import defer
-
from ubuntuone.controlpanel.gui import qt
from ubuntuone.controlpanel.gui.qt import gotoweb as gui
from ubuntuone.controlpanel.gui.qt.tests import (
@@ -32,9 +30,16 @@
class_ui = gui.GoToWebButton
- @defer.inlineCallbacks
- def setUp(self):
- yield super(GoToWebButtonTestCase, self).setUp()
+ def test_uri_default(self):
+ """The uri uses the default."""
+ self.assertEqual(self.ui.uri, self.class_ui.uri)
+
+ def test_text_default(self):
+ """The text uses the default."""
+ if self.class_ui.legend is not None:
+ self.assertEqual(self.ui.text(), self.class_ui.legend)
+ else:
+ self.assertEqual(self.ui.text(), '')
def test_uri_can_be_set(self):
"""The uri can be set."""
@@ -42,10 +47,6 @@
self.ui.uri = uri
self.assertEqual(self.ui.uri, uri)
- def test_layout_direction(self):
- """The layout direction is RightToLeft."""
- self.assertEqual(self.ui.layoutDirection(), gui.QtCore.Qt.RightToLeft)
-
def test_cursor_pointer(self):
"""The cursor is PointingHandCursor."""
self.assertEqual(self.ui.cursor().shape(),
@@ -69,3 +70,17 @@
self.ui.click()
self.assertEqual(self._called, False)
+
+
+class GetStorageButtonTestCase(GoToWebButtonTestCase):
+ """The test suite for the GetStorageButton widget."""
+
+ class_ui = gui.GetStorageButton
+
+ def test_uri(self):
+ """The default uri is correct."""
+ self.assertEqual(self.ui.uri, gui.GET_STORAGE_LINK)
+
+ def test_text(self):
+ """The default legend is correct."""
+ self.assertEqual(self.ui.text(), gui.GET_MORE_STORAGE)
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/gui/qt/tests/test_gui.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/gui/qt/tests/test_gui.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/gui/qt/tests/test_gui.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/gui/qt/tests/test_gui.py 2012-03-21 14:06:28.000000000 +0000
@@ -22,6 +22,17 @@
from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase
+class FakeEntry(object):
+
+ """A fake unity launcher entry."""
+
+ called = None
+
+ def set_property(self, *args, **kwargs):
+ """Fake set_property."""
+ self.called = (args, kwargs)
+
+
class MainWindowTestCase(BaseTestCase):
"""Test the qt main window."""
@@ -58,3 +69,49 @@
self.assertFalse(self.ui.isVisible())
self.assertEqual(self._called,
((), {}), 'close_callback called.')
+
+ def test_switch_to(self):
+ """Check that switch_to changes the current tab"""
+ self.assertEqual(
+ self.ui.ui.control_panel.ui.tab_widget.currentIndex(),
+ self.ui.ui.control_panel.ui.tab_widget.indexOf(
+ self.ui.ui.control_panel.ui.folders_tab))
+ self.ui.switch_to("foobar")
+ self.assertEqual(
+ self.ui.ui.control_panel.ui.tab_widget.currentIndex(),
+ self.ui.ui.control_panel.ui.tab_widget.indexOf(
+ self.ui.ui.control_panel.ui.folders_tab))
+ self.ui.switch_to("devices")
+ self.assertEqual(
+ self.ui.ui.control_panel.ui.tab_widget.currentIndex(),
+ self.ui.ui.control_panel.ui.tab_widget.indexOf(
+ self.ui.ui.control_panel.ui.devices_tab))
+ self.ui.switch_to("settings")
+ self.assertEqual(
+ self.ui.ui.control_panel.ui.tab_widget.currentIndex(),
+ self.ui.ui.control_panel.ui.tab_widget.indexOf(
+ self.ui.ui.control_panel.ui.preferences_tab))
+ self.ui.switch_to("account")
+ self.assertEqual(
+ self.ui.ui.control_panel.ui.tab_widget.currentIndex(),
+ self.ui.ui.control_panel.ui.tab_widget.indexOf(
+ self.ui.ui.control_panel.ui.account_tab))
+ self.ui.switch_to("folders")
+ self.assertEqual(
+ self.ui.ui.control_panel.ui.tab_widget.currentIndex(),
+ self.ui.ui.control_panel.ui.tab_widget.indexOf(
+ self.ui.ui.control_panel.ui.folders_tab))
+
+ def test_focus_in(self):
+ """Test that focusing removes urgent bit from launcher entry."""
+ entry = FakeEntry()
+ self.patch(self.ui, "entry", entry)
+ self.ui.focusInEvent(None)
+ self.assertEqual(entry.called, (('urgent', False), {}))
+
+ def test_set_urgent(self):
+ """Test that set_urgent calls with the right arguments."""
+ entry = FakeEntry()
+ self.patch(self.ui, "entry", entry)
+ self.ui.set_urgent("foo")
+ self.assertEqual(entry.called, (('urgent', "foo"), {}))
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/gui/qt/tests/test_signin.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/gui/qt/tests/test_signin.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/gui/qt/tests/test_signin.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/gui/qt/tests/test_signin.py 2012-03-21 14:06:28.000000000 +0000
@@ -47,11 +47,12 @@
def test_label_is_correct(self):
"""The welcome_label has the correct text."""
- self.assertEqual(self.ui.ui.welcome_label.text(), gui.WELCOME_LABEL)
+ self.assertEqual(unicode(self.ui.ui.welcome_label.text()),
+ gui.WELCOME_MARKUP.format(gui.WELCOME_LABEL))
def test_buttos_are_correct(self):
"""The buttos have the correct text."""
- self.assertEqual(self.ui.ui.login_button.text(),
+ self.assertEqual(unicode(self.ui.ui.login_button.text()),
gui.EXISTING_ACCOUNT_CHOICE_BUTTON)
- self.assertEqual(self.ui.ui.register_button.text(),
+ self.assertEqual(unicode(self.ui.ui.register_button.text()),
gui.SET_UP_ACCOUNT_CHOICE_BUTTON)
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/gui/qt/tests/test_wizard.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/gui/qt/tests/test_wizard.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/gui/qt/tests/test_wizard.py 2012-03-21 14:06:28.000000000 +0000
@@ -22,10 +22,145 @@
from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase, TOKEN
+BUTTONS = [
+ 'BackButton', 'CancelButton', 'CommitButton',
+ 'CustomButton1', 'CustomButton2', 'CustomButton3',
+ 'FinishButton', 'HelpButton', 'NextButton',
+]
+
+
+class AreYouSureTestCase(BaseTestCase):
+ """Test suite for the "Are you sure?" dialog."""
+
+ class_ui = gui.AreYouSure
+
+ def test_title(self):
+ """Check the window title."""
+ self.assertEqual(self.ui.windowTitle(), gui.APP_NAME)
+ self.assertEqual(self.ui.ui.title_label.text(), gui.ARE_YOU_SURE_TITLE)
+
+ def test_message_label(self):
+ """Check the message label text."""
+ link = gui.LINK_STYLE.format(link_url=gui.UBUNTUONE_LINK,
+ link_text=gui.UBUNTUONE_LINK)
+ msg = u'%s%s' % (gui.ARE_YOU_SURE_SUBTITLE,
+ gui.ARE_YOU_SURE_HELP.format(support_url=link))
+ self.assertEqual(unicode(self.ui.ui.message_label.text()), msg)
+
+ def test_buttons(self):
+ """The buttons have the correct text."""
+ self.assertEqual(self.ui.ui.yes_button.text(), gui.ARE_YOU_SURE_YES)
+ self.assertEqual(self.ui.ui.no_button.text(), gui.ARE_YOU_SURE_NO)
+
+
+class UbuntuOnePageTestCase(BaseTestCase):
+ """Test the UbuntuOnePage widget."""
+
+ class_ui = gui.UbuntuOnePage
+ main_title = ''
+ panel_class = gui.QtGui.QFrame
+ sub_title = ''
+
+ def test_panel_class(self):
+ """The panel_class is correct."""
+ self.assertIsInstance(self.ui.panel, self.panel_class)
+
+ def test_panel_is_created(self):
+ """The panel is properly added to the layout."""
+ self.assertEqual(self.ui.layout().itemAt(2).widget(), self.ui.panel)
+
+ def test_title(self):
+ """The title is correct."""
+ self.assertIn(self.main_title, self.ui.title()) # avoid markup
+
+ def test_subtitle(self):
+ """The subtitle is correct."""
+ self.assertEqual(self.ui.subTitle(), self.sub_title)
+
+ def test_error_label(self):
+ """The error label is hidden."""
+ self.assertFalse(self.ui.form_errors_label.isVisible())
+
+ def test_is_final(self):
+ """The page is not final."""
+ # from the doc:
+
+ # After calling setFinalPage(true), isFinalPage() returns true and the
+ # Finish button is visible (and enabled if isComplete() returns true).
+
+ # After calling setFinalPage(false), isFinalPage() returns true if
+ #nextId() returns -1; otherwise, it returns false.
+
+ self.patch(self.ui, 'nextId', lambda *a: 0)
+ self.assertFalse(self.ui.isFinalPage())
+
+
+class LicensePageTestCase(UbuntuOnePageTestCase):
+ """Test the LicensePage wizard page."""
+
+ class_ui = gui.LicensePage
+ panel_class = gui.QtGui.QTextBrowser
+
+ def test_content(self):
+ """The page content is correct."""
+ expected = gui.QtGui.QTextBrowser()
+ expected.setHtml(gui.LICENSE_CONTENT)
+ self.assertEqual(self.ui.panel.toHtml(), expected.toHtml())
+
+
+class SignInPageTestCase(UbuntuOnePageTestCase):
+ """Test the SignInPage wizard page."""
+
+ class_ui = gui.SignInPage
+ panel_class = gui.SignInPanel
+
+
+class CloudToComputerPageTestCase(UbuntuOnePageTestCase):
+ """Test the CloudToComputerPage wizard page."""
+
+ class_ui = gui.CloudToComputerPage
+ main_title = gui.CLOUD_TO_COMPUTER_TITLE
+ panel_class = gui.RemoteFoldersPanel
+ sub_title = gui.CLOUD_TO_COMPUTER_SUBTITLE
+
+ def test_folder_panel_shows_remote_folders_only(self):
+ """The FolderPanel shows only remote folders."""
+ self.assertTrue(self.ui.panel.remote_folders)
+
+
+class SettingsPageTestCase(UbuntuOnePageTestCase):
+ """Test the SettingsPage wizard page."""
+
+ class_ui = gui.SettingsPage
+ panel_class = gui.PreferencesPanel
+
+
+class ComputerToCloudPageTestCase(UbuntuOnePageTestCase):
+ """Test the ComputerToCloudPage wizard page."""
+
+ class_ui = gui.ComputerToCloudPage
+ main_title = gui.COMPUTER_TO_CLOUD_TITLE
+ panel_class = gui.LocalFoldersPanel
+ sub_title = gui.COMPUTER_TO_CLOUD_SUBTITLE
+
+ def test_is_final(self):
+ """The page is not final."""
+ self.assertTrue(self.ui.isFinalPage())
+
+
class UbuntuOneWizardTestCase(BaseTestCase):
- """Test the UbuntuOneWizard."""
+ """Test the UbuntuOneWizard widget."""
class_ui = gui.UbuntuOneWizard
+ confirm_response = gui.QtGui.QDialog.Accepted
+ show_license = False
+
+ @defer.inlineCallbacks
+ def setUp(self):
+ self.patch(self.class_ui, 'show_license', self.show_license)
+ yield super(UbuntuOneWizardTestCase, self).setUp()
+ self.patch(self.ui.confirm_dialog, 'exec_',
+ lambda: self.confirm_response)
def test_options(self):
"""Tne wizard options are correct."""
@@ -40,59 +175,58 @@
"""Tne wizard style is Modern."""
self.assertEqual(self.ui.wizardStyle(), self.ui.ModernStyle)
- def test_cancel_button(self):
- """Send the rejected signal when the cancel button is clicked."""
- button = self.ui.button(self.ui.CancelButton)
- self.assertEqual(button.text(), gui.CLOSE_AND_SETUP_LATER)
+ def test_side_widget(self):
+ """The side widget is correct."""
+ self.assertIsInstance(self.ui.side_widget, gui.SideWidget)
+ self.assertIs(self.ui.sideWidget(), self.ui.side_widget)
- self.ui.rejected.connect(self._set_called)
- button.click()
+ def test_confirm_dialog(self):
+ """The confirm dialog is correct."""
+ self.assertIsInstance(self.ui.confirm_dialog, gui.AreYouSure)
- self.assertEqual(self._called, ((), {}))
+ def test_first_page(self):
+ """The first page is the correct one."""
+ if self.show_license:
+ expected = self.ui.pages[self.ui.license_page]
+ else:
+ expected = self.ui.pages[self.ui.signin_page]
- def test_button_layout(self):
- """The button layout is correct."""
- buttons = [
- 'BackButton', 'CommitButton', 'CustomButton1', 'CustomButton2',
- 'CustomButton3', 'FinishButton', 'HelpButton', 'NextButton',
- ]
- for button_name in buttons:
- button = self.ui.button(getattr(self.ui, button_name))
- self.assertFalse(button.isVisible(),
- 'Button %s should not be visible.' % button_name)
+ self.assertEqual(self.ui.startId(), expected)
- button = self.ui.button(self.ui.CancelButton)
- self.assertTrue(button.isVisible(),
- 'Cancel button should not be visible.')
+ def test_done_accepted(self):
+ """When the wizard reached the end, emit finished."""
+ self.ui.finished.connect(self._set_called)
- def test_first_page(self):
- """The first page is the correct one."""
- self.assertEqual(self.ui.startId(),
- self.ui.pages[self.ui.signin_page])
+ self.ui.done(gui.QtGui.QDialog.Accepted)
- def test_tab_order(self):
- """The button tab order is correct."""
- # can not be tested due to Qt API limitations
+ self.assertEqual(self._called, ((gui.QtGui.QDialog.Accepted,), {}))
-class SideWidgetTestCase(UbuntuOneWizardTestCase):
- """Test the side widget in the wizard."""
+class LicensedUbuntuOneWizardTestCase(UbuntuOneWizardTestCase):
+ """Test the LicensedUbuntuOneWizard."""
- def test_is_there(self):
- """The side widget is correct."""
- self.assertIsInstance(self.ui.side_widget, gui.SideWidget)
- self.assertIs(self.ui.sideWidget(), self.ui.side_widget)
+ show_license = True
-class SignInPageTestCase(UbuntuOneWizardTestCase):
+class UbuntuOneWizardSignInTestCase(UbuntuOneWizardTestCase):
"""Test the SignInPage wizard page."""
+ buttons = {'CancelButton': (gui.CLOSE_AND_SETUP_LATER, 'rejected', ())}
page_name = 'signin'
+ stage_name = page_name
@defer.inlineCallbacks
def setUp(self):
- yield super(SignInPageTestCase, self).setUp()
+ yield super(UbuntuOneWizardSignInTestCase, self).setUp()
self.page = getattr(self.ui, '%s_page' % self.page_name)
+ self._move_to_this_page()
+
+ def _move_to_this_page(self):
+ """Fake the wizard is moved to this page."""
+ page_id = self.ui.pages[self.page]
+ if self.ui.currentId() != page_id:
+ self.ui._next_id = page_id
+ self.ui.next()
def test_was_added(self):
"""The SignInPage is added correctly."""
@@ -103,8 +237,119 @@
"""The page is enabled at startup."""
self.assertTrue(self.page.isEnabled())
+ def test_focus_order(self):
+ """The focus order is correct."""
+ # can not be tested due to Qt API limitations
+
+ def test_button_layout(self):
+ """The button layout is correct."""
+ msg = '%s should not be visible.'
+ for button_name in BUTTONS:
+ button = self.ui.button(getattr(self.ui, button_name))
+ if button_name in self.buttons:
+ self.assertTrue(button.isVisible(),
+ '%s should be visible.' % button_name)
+ else:
+ self.assertFalse(button.isVisible(), msg % button_name)
+
+ def test_side_widget_stage(self):
+ """When in this page, the side_widget' stage is correct."""
+ self.assertEqual(self.ui.side_widget.stage,
+ getattr(self.ui.side_widget, '%s_stage' % self.stage_name))
+
+ def test_buttons_behavior(self):
+ """Send the rejected signal when the cancel button is clicked."""
+ msg = '%r should emit %r with args %r when clicked.'
+
+ for name, (text, signal, signal_args) in self.buttons.iteritems():
+ button = self.ui.button(getattr(self.ui, name))
+
+ if text is not None:
+ self.assertEqual(unicode(button.text()), text)
+
+ getattr(self.ui, signal).connect(self._set_called)
+ button.click()
+
+ self.assertEqual(self._called, (signal_args, {}),
+ msg % (name, signal, signal_args))
+ self._called = False
+
+ def test_done_rejected(self):
+ """When the wizard was cancelled and user confirmed, finish."""
+ self.patch(self, 'confirm_response', gui.QtGui.QDialog.Accepted)
+ self.ui.rejected.connect(self._set_called)
+
+ assert not self.ui.page(self.ui.currentId()).isFinalPage()
+ self.ui.done(gui.QtGui.QDialog.Rejected)
+
+ self.assertEqual(self._called, ((), {}))
+
+ def test_done_rejected_confirmation_rejected(self):
+ """When the wizard was cancelled but user unconfimed, do not finish."""
+ self.patch(self, 'confirm_response', gui.QtGui.QDialog.Rejected)
+ self.ui.accepted.connect(self._set_called)
+ self.ui.rejected.connect(self._set_called)
+ self.ui.finished.connect(self._set_called)
+
+ assert not self.ui.page(self.ui.currentId()).isFinalPage()
+ self.ui.done(gui.QtGui.QDialog.Rejected)
+
+ self.assertEqual(self._called, False)
+
+
+class UbuntuOneWizardCloudToComputerTestCase(UbuntuOneWizardSignInTestCase):
+ """Test the CloudToComputerPage wizard page."""
+
+ buttons = {'NextButton': (None, 'currentIdChanged', (3,))}
+ page_name = 'cloud_folders'
+ stage_name = 'folders'
+
+
+class UbuntuOneWizardSettingsTestCase(UbuntuOneWizardSignInTestCase):
+ """Test the CloudToComputerPage wizard page."""
+
+ buttons = {'BackButton': (None, 'currentIdChanged', (0,))}
+ page_name = 'settings'
+ stage_name = 'folders'
+
-class LoginTestCase(UbuntuOneWizardTestCase):
+class UbuntuOneWizardComputerToCloudTestCase(UbuntuOneWizardSignInTestCase):
+ """Test the CloudToComputerPage wizard page."""
+
+ buttons = {
+ 'FinishButton': (None, 'finished', (gui.QtGui.QDialog.Accepted,)),
+ 'BackButton': (None, 'currentIdChanged', (0,)),
+ }
+ page_name = 'local_folders'
+ stage_name = 'sync'
+
+ def test_done_rejected(self):
+ """When the wizard is closed on the final page, emit rejected."""
+ self.ui.finished.connect(self._set_called)
+
+ assert self.ui.page(self.ui.currentId()).isFinalPage()
+ self.ui.done(gui.QtGui.QDialog.Rejected)
+
+ self.assertEqual(self._called, ((gui.QtGui.QDialog.Rejected,), {}))
+
+ def test_done_rejected_confirmation_rejected(self):
+ """When the wizard was cancelled but user unconfimed, do not finish."""
+ # does not apply to this page
+
+
+class UbuntuOneWizardLicensePage(UbuntuOneWizardSignInTestCase):
+ """Test the LicensePage wizard page."""
+
+ buttons = {
+ 'NextButton': (gui.LICENSE_AGREE, 'currentIdChanged', (1,)),
+ 'CancelButton': (gui.LICENSE_DISAGREE, 'rejected', ()),
+ }
+ page_name = 'license'
+ show_license = True
+ stage_name = 'install'
+
+
+class UbuntuOneWizardLoginTestCase(UbuntuOneWizardTestCase):
"""Test the login through the wizard."""
method = 'login'
@@ -112,7 +357,7 @@
@defer.inlineCallbacks
def test_with_credentials(self):
"""Wizard is done when credentials were retrieved."""
- self.ui.finished.connect(self._set_called)
+ self.ui.currentIdChanged.connect(self._set_called)
d = defer.succeed(TOKEN)
def check():
@@ -128,12 +373,13 @@
yield d
self.assertTrue(self.ui.signin_page.isEnabled())
- self.assertEqual(self._called, ((1,), {}))
+ expected_next_id = self.ui.pages[self.ui.cloud_folders_page]
+ self.assertEqual(self._called, ((expected_next_id,), {}))
@defer.inlineCallbacks
def test_without_credentials(self):
"""Wizard is done when credentials were retrieved."""
- self.ui.finished.connect(self._set_called)
+ self.ui.currentIdChanged.connect(self._set_called)
d = defer.succeed(None)
def check():
@@ -152,7 +398,7 @@
self.assertFalse(self._called)
-class RegisterTestCase(LoginTestCase):
+class UbuntuOneWizardRegisterTestCase(UbuntuOneWizardLoginTestCase):
"""Test the register through the wizard."""
method = 'register'
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/gui/qt/wizard.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/gui/qt/wizard.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/gui/qt/wizard.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/gui/qt/wizard.py 2012-03-21 14:06:28.000000000 +0000
@@ -19,26 +19,116 @@
from PyQt4 import QtGui, QtCore
from twisted.internet import defer
+from ubuntu_sso.qt import LINK_STYLE
+from ubuntu_sso.qt.sso_wizard_page import BaseWizardPage
from ubuntu_sso.utils.ui import CLOSE_AND_SETUP_LATER
from ubuntuone.controlpanel import cache
+from ubuntuone.controlpanel.logger import log_call, setup_logging
+from ubuntuone.controlpanel.gui import (
+ APP_NAME,
+ ARE_YOU_SURE_HELP,
+ ARE_YOU_SURE_NO,
+ ARE_YOU_SURE_SUBTITLE,
+ ARE_YOU_SURE_TITLE,
+ ARE_YOU_SURE_YES,
+ CLOUD_TO_COMPUTER_SUBTITLE,
+ CLOUD_TO_COMPUTER_TITLE,
+ COMPUTER_TO_CLOUD_SUBTITLE,
+ COMPUTER_TO_CLOUD_TITLE,
+ LICENSE_AGREE,
+ LICENSE_AGREEMENT,
+ LICENSE_BASIC,
+ LICENSE_DISAGREE,
+ LICENSE_GPL3,
+ LICENSE_LINK,
+ UBUNTUONE_LINK,
+)
+from ubuntuone.controlpanel.gui.qt.folders import (
+ RemoteFoldersPanel,
+ LocalFoldersPanel,
+)
+from ubuntuone.controlpanel.gui.qt.preferences import PreferencesPanel
from ubuntuone.controlpanel.gui.qt.signin import SignInPanel
from ubuntuone.controlpanel.gui.qt.side_widget import SideWidget
+from ubuntuone.controlpanel.gui.qt.ui import are_you_sure_ui
-class UbuntuOnePage(QtGui.QWizardPage):
+logger = setup_logging('qt.wizard')
+
+
+GPL_URL = u'http://www.gnu.org/licenses'
+GPL_LINK = LINK_STYLE.format(link_url=GPL_URL, link_text=GPL_URL)
+LICENSE_CONTENT = u"""
+
+ {license_agreement}
+ {license_gpl3}
+ {license_basic}
+ {license_link}
+
+""".format(license_agreement=LICENSE_AGREEMENT,
+ license_gpl3=LICENSE_GPL3, license_basic=LICENSE_BASIC,
+ license_link=LICENSE_LINK.format(license_link=GPL_LINK),
+)
+
+
+class AreYouSure(QtGui.QDialog):
+
+ """A 'Are you sure?' dialog."""
+
+ def __init__(self, *args, **kwargs):
+ super(AreYouSure, self).__init__(*args, **kwargs)
+ self.ui = are_you_sure_ui.Ui_Dialog()
+ self.ui.setupUi(self)
+ self.setWindowTitle(APP_NAME)
+
+ self.ui.title_label.setText(ARE_YOU_SURE_TITLE)
+
+ link = LINK_STYLE.format(link_url=UBUNTUONE_LINK,
+ link_text=UBUNTUONE_LINK)
+ msg = u'%s%s' % (ARE_YOU_SURE_SUBTITLE,
+ ARE_YOU_SURE_HELP.format(support_url=link))
+ self.ui.message_label.setText(msg)
+
+ self.ui.yes_button.setText(ARE_YOU_SURE_YES)
+ self.ui.no_button.setText(ARE_YOU_SURE_NO)
+
+
+class UbuntuOnePage(BaseWizardPage):
"""A generic page for the UbuntuOneWizard."""
- panel_class = None
+ is_final = False
+ main_title = None
+ max_width = 5000
+ panel_class = QtGui.QFrame
+ sub_title = None
def __init__(self, *args, **kwargs):
super(UbuntuOnePage, self).__init__(*args, **kwargs)
- self.layout = QtGui.QVBoxLayout(self)
self.panel = None
if self.panel_class is not None:
- self.panel = SignInPanel()
- self.layout.addWidget(self.panel)
+ self.panel = self.panel_class()
+ self.layout().addWidget(self.panel)
+
+ if self.main_title is not None:
+ self.setTitle(self.main_title)
+
+ if self.sub_title is not None:
+ self.setSubTitle(self.sub_title)
+
+ self.form_errors_label.hide()
+ self.setFinalPage(self.is_final)
+
+
+class LicensePage(UbuntuOnePage):
+ """The page to show the license."""
+
+ panel_class = QtGui.QTextBrowser
+
+ def __init__(self, *args, **kwargs):
+ super(LicensePage, self).__init__(*args, **kwargs)
+ self.panel.setHtml(LICENSE_CONTENT)
class SignInPage(UbuntuOnePage):
@@ -47,9 +137,38 @@
panel_class = SignInPanel
+class CloudToComputerPage(UbuntuOnePage):
+ """The page to choose cloud folders to sync locally."""
+
+ main_title = CLOUD_TO_COMPUTER_TITLE
+ panel_class = RemoteFoldersPanel
+ sub_title = CLOUD_TO_COMPUTER_SUBTITLE
+
+ def __init__(self, *args, **kwargs):
+ super(CloudToComputerPage, self).__init__(*args, **kwargs)
+ self.panel.ui.add_folder_button.hide()
+
+
+class SettingsPage(UbuntuOnePage):
+ """The page to adjust the service settings."""
+
+ panel_class = PreferencesPanel
+
+
+class ComputerToCloudPage(UbuntuOnePage):
+ """The page to choose local folders to sync remotly."""
+
+ is_final = True
+ main_title = COMPUTER_TO_CLOUD_TITLE
+ panel_class = LocalFoldersPanel
+ sub_title = COMPUTER_TO_CLOUD_SUBTITLE
+
+
class UbuntuOneWizard(cache.Cache, QtGui.QWizard):
"""The Ubuntu One wizard."""
+ show_license = False # do not change unless you know what you're doing
+
def __init__(self, *args, **kwargs):
super(UbuntuOneWizard, self).__init__(*args, **kwargs)
self.pages = {}
@@ -58,13 +177,19 @@
self.setOption(self.HaveFinishButtonOnEarlyPages, False)
self.setWizardStyle(self.ModernStyle)
- self.setButtonText(self.CancelButton, CLOSE_AND_SETUP_LATER)
- self.setButtonLayout([self.Stretch, self.CancelButton])
+ self.confirm_dialog = AreYouSure(self)
self.side_widget = SideWidget()
self.side_widget.stage = self.side_widget.signin_stage
self.setSideWidget(self.side_widget)
+ # license
+ self.license_page = LicensePage()
+ self.next_button_text = self.button(self.NextButton).text()
+ if self.show_license:
+ self.addPage(self.license_page)
+
+ # sign in
self.signin_page = SignInPage()
self.addPage(self.signin_page)
@@ -72,10 +197,23 @@
self.signin_page.panel.ui.register_button.clicked.connect(
self.register)
- self.setTabOrder(self.signin_page.panel.ui.login_button,
- self.signin_page.panel.ui.register_button)
- self.setTabOrder(self.signin_page.panel.ui.register_button,
- self.button(self.CancelButton))
+ # cloud to compuer
+ self.cloud_folders_page = CloudToComputerPage()
+ self.addPage(self.cloud_folders_page)
+
+ self.cloud_folders_page.panel.ui.check_settings_button.clicked.connect(
+ self.check_settings)
+
+ # settings
+ self.settings_page = SettingsPage()
+ self.addPage(self.settings_page)
+
+ # computer to cloud
+ self.local_folders_page = ComputerToCloudPage()
+ self.addPage(self.local_folders_page)
+
+ self._next_id = self.pages[self.signin_page]
+ self.next()
# pylint: disable=C0103
@@ -84,8 +222,86 @@
page_id = super(UbuntuOneWizard, self).addPage(page)
self.pages[page] = page_id
+ @log_call(logger.info)
+ def initializePage(self, page_id):
+ """The wizard will show the page 'page_id'."""
+ page = self.page(page_id)
+ logger.debug('UbuntuOneWizard.initializePage: page is %r.', page)
+
+ button_layout = button_to = button = stage = None
+
+ if page is self.license_page:
+ button_layout = [self.Stretch, self.CancelButton, self.NextButton]
+ button = self.button(self.NextButton)
+ button_to = self.button(self.CancelButton)
+ stage = self.side_widget.install_stage
+
+ self.setButtonText(self.NextButton, LICENSE_AGREE)
+ self.setButtonText(self.CancelButton, LICENSE_DISAGREE)
+
+ elif page is self.signin_page:
+ button_layout = [self.Stretch, self.CancelButton]
+ button = self.signin_page.panel.ui.register_button
+ button_to = self.button(self.CancelButton)
+ stage = self.side_widget.signin_stage
+ self._next_id = self.pages[self.cloud_folders_page]
+
+ self.setButtonText(self.CancelButton, CLOSE_AND_SETUP_LATER)
+ self.setTabOrder(self.signin_page.panel.ui.login_button, button)
+
+ elif page is self.cloud_folders_page:
+ self.setButtonText(self.NextButton, self.next_button_text)
+
+ button_layout = [self.Stretch, self.NextButton]
+ button = self.cloud_folders_page.panel.ui.check_settings_button
+ button_to = self.button(self.NextButton)
+ stage = self.side_widget.folders_stage
+ self._next_id = self.pages[self.local_folders_page]
+ elif page is self.settings_page:
+ button_layout = [self.Stretch, self.BackButton]
+ button = self.settings_page.panel.ui.apply_changes_button
+ button_to = self.button(self.BackButton)
+ stage = self.side_widget.folders_stage
+ self._next_id = self.pages[self.cloud_folders_page]
+ elif page is self.local_folders_page:
+ button_layout = [self.Stretch, self.BackButton, self.FinishButton]
+ button = self.local_folders_page.panel.ui.add_folder_button
+ button_to = self.button(self.BackButton)
+ stage = self.side_widget.sync_stage
+ else:
+ logger.error('UbuntuOneWizard.initializePage was called with an'
+ 'unknown page: %r (page_id was %r).', page, page_id)
+
+ logger.info('UbuntuOneWizard.initializePage: new page is %r, '
+ 'new button layout is %r, '
+ 'new side widget stage is %r.', page, button_layout, stage)
+
+ if button is not None and button_to is not None:
+ self.setTabOrder(button, button_to)
+ if button_layout is not None:
+ self.setButtonLayout(button_layout)
+ if stage is not None:
+ self.side_widget.stage = stage
+
+ @log_call(logger.info)
+ def cleanupPage(self, page_id):
+ """Called clean up 'page_id' just before the user leaves it."""
+ page = self.page(page_id)
+ logger.debug('UbuntuOneWizard.cleanupPage: page is %r.', page)
+ if page is self.settings_page or page is self.local_folders_page:
+ self.initializePage(self.pages[self.cloud_folders_page])
+
+ def nextId(self):
+ """Return the nextId to show."""
+ return self._next_id
+
# pylint: enable=C0103
+ def _process_credentials(self, credentials=None):
+ """Confirm which is the next step after analyzing 'credentials'."""
+ if credentials:
+ self.next()
+
@QtCore.pyqtSlot()
@defer.inlineCallbacks
def login(self):
@@ -104,7 +320,32 @@
self._process_credentials(credentials)
self.setEnabled(True)
- def _process_credentials(self, credentials=None):
- """Confirm which is the next step after analyzing 'credentials'."""
- if credentials:
- self.accept()
+ @QtCore.pyqtSlot()
+ def check_settings(self):
+ """Show the check settings page."""
+ self._next_id = self.pages[self.settings_page]
+ self.next()
+
+ def done(self, result):
+ """The main window is being closed, call any custom callback."""
+ if result == QtGui.QDialog.Accepted:
+ parent_done = super(UbuntuOneWizard, self).done
+ f = lambda: parent_done(QtGui.QDialog.Accepted)
+ self.local_folders_page.panel.changesApplied.connect(f)
+ # commit local_folders_page's changes
+ self.local_folders_page.panel.apply_changes()
+ elif not self.page(self.currentId()).isFinalPage():
+ response = self.confirm_dialog.exec_()
+ if response == QtGui.QDialog.Accepted:
+ logger.warning('UbuntuOneWizard: user canceled setup.')
+ self.rejected.emit()
+ elif (self.show_license and
+ self.currentId() == self.pages[self.license_page]):
+ response = self.confirm_dialog.exec_()
+ if response == QtGui.QDialog.Accepted:
+ logger.warning('UbuntuOneWizard: user wants to uninstall.')
+ # TODO: needs implementation in this project
+ ##qt.utils.uninstall_application()
+ self.rejected.emit()
+ else:
+ super(UbuntuOneWizard, self).done(result)
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/gui/tests/__init__.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/gui/tests/__init__.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/gui/tests/__init__.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/gui/tests/__init__.py 2012-03-21 14:06:28.000000000 +0000
@@ -134,6 +134,7 @@
next_result = None
exposed_methods = []
exposed_results = {}
+ raise_attr_error = True
def __init__(self, *args, **kwargs):
self._args = args
@@ -142,6 +143,23 @@
for i in self.exposed_methods:
setattr(self, i, self._record_call(i))
+ def __call__(self, *args, **kwargs):
+ """Skip."""
+
+ def __getattribute__(self, attr_name):
+ super_getattr = super(FakedObject, self).__getattribute__
+
+ try:
+ result = super_getattr(attr_name)
+ except AttributeError:
+ if super_getattr('raise_attr_error'):
+ raise
+ else:
+ result = FakedObject()
+ result.raise_attr_error = super_getattr('raise_attr_error')
+
+ return result
+
def _record_call(self, func_name):
"""Store values when calling 'func_name'."""
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/logger.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/logger.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/logger.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/logger.py 2012-03-21 14:06:28.000000000 +0000
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
-
-# Authors: Natalia B Bidart
#
-# Copyright 2010 Canonical Ltd.
+# Copyright 2010-2012 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
@@ -30,7 +28,7 @@
from ubuntuone.platform.xdg_base_directory import ubuntuone_log_dir
-if os.environ.get('DEBUG'):
+if os.environ.get('U1_DEBUG'):
LOG_LEVEL = logging.DEBUG
else:
# Only log this level and above
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/tests/__init__.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/tests/__init__.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/tests/__init__.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/tests/__init__.py 2012-03-21 14:06:28.000000000 +0000
@@ -334,6 +334,15 @@
]
+class CustomError(Exception):
+ """Custom error for tests."""
+
+
+def helper_fail(*a, **kw):
+ """Helper to raise an exception, usually used when monkey-patching."""
+ raise CustomError((a, kw))
+
+
class TestCase(BaseTestCase):
"""Basics for testing."""
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/tests/test_login_client.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/tests/test_login_client.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/tests/test_login_client.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/tests/test_login_client.py 2012-03-21 14:06:28.000000000 +0000
@@ -21,11 +21,7 @@
from twisted.internet import defer
from ubuntuone.controlpanel import login_client
-from ubuntuone.controlpanel.tests import TestCase, TOKEN
-
-
-class CustomError(Exception):
- """Custom error for tests."""
+from ubuntuone.controlpanel.tests import CustomError, TestCase, TOKEN
class FakedCredentialsManagementTool(object):
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/tests/test_sd_client.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/tests/test_sd_client.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/tests/test_sd_client.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/tests/test_sd_client.py 2012-03-21 14:06:28.000000000 +0000
@@ -32,7 +32,7 @@
from ubuntuone.syncdaemon.interaction_interfaces import bool_str
from ubuntuone.controlpanel import sd_client
-from ubuntuone.controlpanel.tests import TestCase
+from ubuntuone.controlpanel.tests import CustomError, TestCase
# Instance of 'SyncDaemonTool' has no 'foo' member
# pylint: disable=E1101
@@ -41,10 +41,6 @@
SAMPLE_LIMITS = {'upload': 999, 'download': 838}
-class CustomError(Exception):
- """Custom error for tests."""
-
-
class FakedSyncDaemonTool(object):
"""Fake the SyncDaemonTool."""
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/tests/test_web_client.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/tests/test_web_client.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/tests/test_web_client.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/tests/test_web_client.py 2012-03-21 14:06:28.000000000 +0000
@@ -29,6 +29,7 @@
WebClient,
WebClientError,
)
+from ubuntuone.platform.credentials import APP_NAME
SAMPLE_KEY = "result"
@@ -104,6 +105,10 @@
self.wc = WebClient(sample_get_credentials, base_url=self.base_iri)
self.addCleanup(self.wc.shutdown)
+ def test_correct_app_name(self):
+ """Assert that the wc uses the correct appname."""
+ self.assertEqual(APP_NAME, self.wc.wc.appname)
+
@defer.inlineCallbacks
def test_get_url(self):
"""A method is successfully called in the mock webservice."""
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/utils/__init__.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/utils/__init__.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/utils/__init__.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/utils/__init__.py 2012-03-21 14:06:28.000000000 +0000
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
-
#
-# Copyright 2010 Canonical Ltd.
+# Copyright 2010-2012 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
@@ -37,10 +36,14 @@
if sys.platform == 'win32':
from ubuntuone.controlpanel.utils import windows
are_updates_present = windows.are_updates_present
+ default_folders = windows.default_folders
perform_update = windows.perform_update
else:
+ from ubuntuone.controlpanel.utils import linux
are_updates_present = lambda *args, **kwargs: False
+ default_folders = linux.default_folders
perform_update = lambda *args, **kwargs: None
+
# pylint: enable=C0103
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/utils/linux.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/utils/linux.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/utils/linux.py 1970-01-01 00:00:00.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/utils/linux.py 2012-03-21 14:06:28.000000000 +0000
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2012 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 3, as published
+# by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranties of
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see .
+
+"""Miscelaneous functions and constants for linux."""
+
+import codecs
+import os
+
+from dirspec.basedir import xdg_config_home
+from ubuntuone.controlpanel.logger import setup_logging
+
+
+logger = setup_logging('utils.linux')
+
+
+def default_folders(user_home='', dirs_path=None):
+ """Return a list of the folders to add by default."""
+ result = []
+
+ if dirs_path is None:
+ dirs_path = os.path.join(xdg_config_home, u'user-dirs.dirs')
+
+ if not os.path.exists(dirs_path):
+ logger.warning('default_folders: dirs_path %r does not exist.',
+ dirs_path)
+ return result
+
+ # pylint: disable=W0702
+
+ try:
+ with codecs.open(dirs_path, encoding='utf-8') as f:
+ for line in f:
+ if line.startswith(u'#'):
+ continue
+
+ try:
+ _, value = line.strip().split(u'=')
+ value = value.strip(u'"').replace(u'$HOME', user_home)
+ except:
+ logger.exception('default_folders: can not row %r:', line)
+ else:
+ result.append(value)
+ except:
+ logger.exception('default_folders: can not load file %r:', dirs_path)
+
+ return result
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/utils/tests/test_linux.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/utils/tests/test_linux.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/utils/tests/test_linux.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/utils/tests/test_linux.py 2012-03-21 14:06:28.000000000 +0000
@@ -17,11 +17,35 @@
"""Test the linux utils functions."""
+import codecs
+import os
from twisted.internet import defer
from ubuntuone.controlpanel import utils
-from ubuntuone.controlpanel.tests import TestCase
+from ubuntuone.controlpanel.tests import TestCase, USER_HOME
+
+REAL_CONTENT = u"""
+XDG_DESKTOP_DIR="$HOME/Desktop"
+XDG_DOWNLOAD_DIR="$HOME/Downloads"
+XDG_PUBLICSHARE_DIR="$HOME/Public"
+XDG_DOCUMENTS_DIR="$HOME/Documents"
+XDG_MUSIC_DIR="$HOME/Music"
+XDG_PICTURES_DIR="$HOME/Pictures"
+XDG_VIDEOS_DIR="$HOME/Videos"
+"""
+
+NON_ASCII = u"""
+XDG_DESKTOP_DIR="$HOME/桌面"
+XDG_DOWNLOAD_DIR="$HOME/下载"
+XDG_DOCUMENTS_DIR="$HOME/文档"
+"""
+
+LATIN_1 = u"""
+XDG_DESKTOP_DIR="$HOME/Súbeme"
+XDG_DOWNLOAD_DIR="$HOME/Bájame"
+XDG_DOCUMENTS_DIR="$HOME/Mis ñoños Documentos"
+"""
class AutoupdaterTestCase(TestCase):
@@ -36,3 +60,79 @@
def test_perform_update(self):
"""Test the method that performs the update."""
self.assertEqual(None, utils.perform_update())
+
+
+class DefaultFoldersTestCase(TestCase):
+ """Test suite for the default_folders retriever."""
+
+ def _create_faked_file(self, content='', encoding=None):
+ """Create a faked file to be parsed and clean it up afterwards."""
+ filename = './dirs-parser-test.dirs'
+ if os.path.exists(filename):
+ os.remove(filename)
+
+ if encoding is None:
+ encoding = 'utf-8'
+
+ with codecs.open(filename, 'w', encoding=encoding) as f:
+ f.write(content)
+
+ self.addCleanup(os.remove, filename)
+
+ return filename
+
+ def test_default_folders_not_file(self):
+ """Default folders are empty if the file does not exist."""
+ path = 'foo/bar/baz/yadda/yo'
+ assert not os.path.exists(path)
+ actual = utils.default_folders(dirs_path=path)
+
+ self.assertEqual(actual, [])
+
+ def test_default_folders_empty_file(self):
+ """Default folders are empty if the file empty."""
+ filename = self._create_faked_file(content='')
+ actual = utils.default_folders(dirs_path=filename)
+
+ self.assertEqual(actual, [])
+
+ def test_default_folders_only_comments(self):
+ """Default folders are empty if only comments in the file."""
+ content = u"# yadda yadda\n# foo bar baz\n#puipuipui"
+ filename = self._create_faked_file(content=content)
+ actual = utils.default_folders(dirs_path=filename)
+
+ self.assertEqual(actual, [])
+
+ def test_default_folders_syntax_error(self):
+ """Default folders do not explode on syntax error."""
+ content = u"foo ~ bar\nYADDA=DOO"
+ filename = self._create_faked_file(content=content)
+ actual = utils.default_folders(dirs_path=filename)
+
+ self.assertEqual(actual, [u'DOO'])
+
+ def test_default_folders_parsed(self):
+ """Default folders parses the content of the file."""
+ filename = self._create_faked_file(content=REAL_CONTENT)
+ actual = utils.default_folders(user_home=USER_HOME, dirs_path=filename)
+
+ expected = (i.split(u'=')[1].strip(u'"') for i in REAL_CONTENT.split())
+ expected = [i.replace(u'$HOME', USER_HOME) for i in expected]
+ self.assertEqual(actual, expected)
+
+ def test_default_folders_non_ascii(self):
+ """Default folders parses the content if it's non-ascii."""
+ filename = self._create_faked_file(content=NON_ASCII)
+ actual = utils.default_folders(user_home=USER_HOME, dirs_path=filename)
+
+ expected = (i.split(u'=')[1].strip(u'"') for i in NON_ASCII.split())
+ expected = [i.replace(u'$HOME', USER_HOME) for i in expected]
+ self.assertEqual(actual, expected)
+
+ def test_default_folders_bad_encoding(self):
+ """Default folders parses the content if it's non-ascii."""
+ filename = self._create_faked_file(content=LATIN_1, encoding='latin-1')
+ actual = utils.default_folders(user_home=USER_HOME, dirs_path=filename)
+
+ self.assertEqual(actual, [])
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/utils/tests/test_windows.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/utils/tests/test_windows.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/utils/tests/test_windows.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/utils/tests/test_windows.py 2012-03-21 14:06:28.000000000 +0000
@@ -87,6 +87,47 @@
self.assertEqual(0, self._called[0][5])
+class DefaultFoldersTestCase(TestCase):
+ """Test the default_folders method."""
+
+ def test_special_folders(self, names=None):
+ """Test that default_folders does the right thing."""
+ folders = utils.default_folders()
+
+ if names is None:
+ names = ('PERSONAL', 'MYMUSIC', 'MYPICTURES', 'MYVIDEO')
+
+ self.assertEqual(len(folders), len(names))
+ self.assertEqual(folders, [os.path.abspath(f) for f in folders])
+
+ expected = []
+ for name in names:
+ name = getattr(utils.windows.shellcon, 'CSIDL_%s' % name)
+ folder = utils.windows.shell.SHGetFolderPath(0, name, None, 0)
+ expected.append(folder.encode('utf8'))
+
+ self.assertEqual(sorted(folders), sorted(expected))
+
+ def test_special_folders_with_error(self):
+ """Test that default_folders handles errors."""
+ failing_name = 'MYVIDEO'
+ original_func = utils.windows.shell.SHGetFolderPath
+
+ class SomeError(Exception):
+ """For test only."""
+
+ def fail_for_some(code, name, parent, flag):
+ """Custom failer."""
+ fail = getattr(utils.windows.shellcon, 'CSIDL_%s' % failing_name)
+ if name == fail:
+ raise SomeError('The parameter is incorrect.')
+ else:
+ return original_func(code, name, parent, flag)
+
+ self.patch(utils.windows.shell, 'SHGetFolderPath', fail_for_some)
+ self.test_special_folders(names=('PERSONAL', 'MYMUSIC', 'MYPICTURES'))
+
+
class GetPathTestCase(TestCase):
"""Test the code that is used for the auto update process."""
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/utils/windows.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/utils/windows.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/utils/windows.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/utils/windows.py 2012-03-21 14:06:28.000000000 +0000
@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
-"""Autoupdate functions for windows."""
+"""Miscelaneous functions and constants for windows."""
import os
import sys
@@ -23,14 +23,15 @@
# Avoid pylint error on Linux
# pylint: disable=F0401
import win32api
-# pylint: enable=F0401
+from win32com.shell import shell, shellcon
+# pylint: enable=F0401
from twisted.internet import defer
from twisted.internet.utils import getProcessValue
from ubuntuone.controlpanel.logger import setup_logging
-logger = setup_logging('utils')
+logger = setup_logging('utils.windows')
AUTOUPDATE_EXE_NAME = 'autoupdate-windows.exe'
@@ -63,6 +64,30 @@
defer.returnValue(result)
+def default_folders(user_home=None):
+ """Return a list of the folders to add by default."""
+ # as per http://msdn.microsoft.com/en-us/library/windows/desktop/bb762181,
+ # SHGetFolderPath is deprecated, we should migrate to SHGetKnownFolderPath
+ # (http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188)
+ # but the latter does not support XP
+ # (Minimum supported client: Windows Vista)
+ get_path = lambda name: shell.SHGetFolderPath(
+ 0, getattr(shellcon, name), None, 0).encode('utf8')
+
+ folders = []
+ # More information on these constants at
+ # http://msdn.microsoft.com/en-us/library/bb762494
+ for name in (u'PERSONAL', u'MYMUSIC', u'MYPICTURES', u'MYVIDEO'):
+ name = u'CSIDL_%s' % name
+ try:
+ folders.append(get_path(name))
+ except: # pylint: disable=W0702
+ logger.exception('default_folders: could not retrieve folder info '
+ 'for name %r.', name)
+
+ return folders
+
+
def perform_update():
"""Spawn the autoupdate process and call the stop function."""
update_path = _get_update_path()
diff -Nru ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/web_client.py ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/web_client.py
--- ubuntuone-control-panel-2.99.90/ubuntuone/controlpanel/web_client.py 2012-03-06 18:18:50.000000000 +0000
+++ ubuntuone-control-panel-2.99.91/ubuntuone/controlpanel/web_client.py 2012-03-21 14:06:28.000000000 +0000
@@ -30,7 +30,7 @@
from ubuntuone.controlpanel import WEBSERVICE_BASE_URL
from ubuntuone.controlpanel.logger import setup_logging
-
+from ubuntuone.platform.credentials import APP_NAME
logger = setup_logging('webclient')
@@ -42,7 +42,7 @@
"""Initialize the webclient."""
self.base_url = base_url
self.get_credentials = get_credentials
- self.wc = webclient_factory()
+ self.wc = webclient_factory(APP_NAME)
logger.debug("WebClient created: base_url is %r, inner client is %r.",
self.base_url, self.wc)