~ubuntu-branches/ubuntu/quantal/ubuntuone-control-panel/quantal-proposed

« back to all changes in this revision

Viewing changes to ubuntuone/controlpanel/gui/qt/tests/test_folders.py

  • Committer: Package Import Robot
  • Author(s): Rodney Dawes
  • Date: 2012-06-20 16:42:13 UTC
  • mto: This revision was merged to the branch mainline in revision 50.
  • Revision ID: package-import@ubuntu.com-20120620164213-6z7q5pqc0ydjrp19
Tags: upstream-3.99.0
Import upstream version 3.99.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
import shutil
25
25
import sys
26
26
 
27
 
from PyQt4 import QtGui
 
27
from PyQt4 import QtCore, QtGui
28
28
from twisted.internet import defer
29
29
from ubuntuone.devtools.handlers import MementoHandler
30
30
from ubuntuone.devtools.testcases import skipIfOS
34
34
    FAKE_VOLUMES_INFO,
35
35
    FAKE_VOLUMES_MINIMAL_INFO,
36
36
    FAKE_VOLUMES_NO_FREE_SPACE_INFO,
37
 
    MUSIC_FOLDER, ROOT, USER_HOME,
 
37
    FAKE_VOLUMES_ONLY_ROOT_INFO,
 
38
    MUSIC_FOLDER, MUSIC_PATH, ROOT, USER_HOME,
38
39
)
 
40
from ubuntuone.controlpanel.gui import MUSIC_REAL_PATH
39
41
from ubuntuone.controlpanel.gui.qt import folders as gui
40
42
from ubuntuone.controlpanel.gui.qt.tests import (
41
43
    BaseTestCase,
 
44
    FakedControlPanelBackend,
42
45
    FakedDialog,
 
46
    SAMPLE_ACCOUNT_INFO as ACCOUNT_INFO,
43
47
)
44
48
from ubuntuone.controlpanel.gui.qt.tests.test_ubuntuonebin import (
45
49
    UbuntuOneBinTestCase,
49
53
# Instance of 'ControlBackend' has no '_called' member
50
54
# pylint: disable=W0212, E1103
51
55
 
 
56
DEFAULT_FOLDERS = [u'Music', u'Configuración', 'An invalid folder',
 
57
    os.path.join(u'I ♥ Ubuntu', u'Documents'),
 
58
    os.path.join(u'I ♥ Ubuntu', u'Something', u'Long'),
 
59
]
 
60
 
52
61
 
53
62
def volumes_with_music_unsubscribed():
54
63
    """Return a copy of FAKE_VOLUMES_MINIMAL_INFO with music unsubscribed."""
66
75
    return name
67
76
 
68
77
 
 
78
def size_for_path(path):
 
79
    """Fake size calulation for paths."""
 
80
    return sum(map(ord, path)) * len(path) * 10000
 
81
 
 
82
 
 
83
class FakedCalculateSize(object):
 
84
    """A faked CalculateSize thread."""
 
85
 
 
86
    next_size = None
 
87
 
 
88
    def __init__(self, path, queue):
 
89
        self.path = path
 
90
        self.queue = queue
 
91
        self.started = False
 
92
 
 
93
    def start(self):
 
94
        """Fake start."""
 
95
        self.started = True
 
96
        if self.next_size is None:
 
97
            size = size_for_path(self.path)
 
98
        else:
 
99
            size = self.next_size
 
100
        self.queue.put([self.path, size])
 
101
 
 
102
 
 
103
class FakedTimer(gui.QtCore.QTimer):
 
104
    """A faked timer."""
 
105
 
 
106
    def __init__(self, *args, **kwargs):
 
107
        super(FakedTimer, self).__init__(*args, **kwargs)
 
108
        self._active = False
 
109
        # pylint: disable=C0103
 
110
        self.isActive = lambda: self._active
 
111
 
 
112
    def start(self, interval):
 
113
        """Override start."""
 
114
        self._active = True
 
115
 
 
116
    def stop(self):
 
117
        """Override stop."""
 
118
        self._active = False
 
119
 
 
120
 
69
121
class ExploreFolderButtonTestCase(BaseTestCase):
70
122
    """Test the ExploreFolderButton widget."""
71
123
 
74
126
 
75
127
    def test_text(self):
76
128
        """The button text is correct."""
77
 
        self.assertEqual(unicode(self.ui.text()),
78
 
            gui.FOLDERS_COLUMN_EXPLORE)
 
129
        self.assertEqual(unicode(self.ui.text()), gui.FOLDERS_COLUMN_EXPLORE)
79
130
 
80
131
    def test_clicked(self):
81
132
        """Clicking the button opens the folder in the default file manager."""
193
244
 
194
245
    def test_process_info(self):
195
246
        """The volumes info is processed when ready."""
 
247
        folders = self.ui.ui.folders
196
248
        self.ui.process_info(FAKE_VOLUMES_INFO)
197
 
        folders = self.ui.ui.folders
198
 
 
199
249
        root = folders.invisibleRootItem()
200
250
        self.assertEqual(len(FAKE_VOLUMES_INFO), root.childCount())
201
251
 
202
 
        treeiter = gui.QtGui.QTreeWidgetItemIterator(folders)
203
252
        for name, _, volumes in FAKE_VOLUMES_INFO:
204
253
            name = _build_name(name)
205
 
 
206
 
            # get first child, matching "My Folders"
207
 
            item = treeiter.value()
208
 
            self.assertFalse(item.is_empty)
209
 
 
 
254
            items = folders.findItems(name, QtCore.Qt.MatchFixedString
 
255
                | QtCore.Qt.MatchCaseSensitive | QtCore.Qt.MatchRecursive,
 
256
                gui.FOLDER_NAME_COL)
 
257
            self.assertEqual(1, len(items))
 
258
            item = items[0]
210
259
            self.assert_folder_group_header_correct(item, name)
211
 
 
212
260
            self.assertEqual(len(volumes), item.childCount())
213
 
            sorted_vols = sorted(volumes, key=operator.itemgetter('path'))
214
 
            for volume in sorted_vols:
215
 
                treeiter += 1
216
 
                item = treeiter.value()  # get child folder
217
 
                self.assertTrue(item is not None)
 
261
            for volume in volumes:
218
262
 
219
263
                name = volume['path'].replace(USER_HOME + os.path.sep, '')
220
264
                expected_path = volume['path']
227
271
                else:
228
272
                    icon_name = gui.SHARE_ICON_NAME
229
273
 
 
274
                items = folders.findItems(name, QtCore.Qt.MatchFixedString |
 
275
                    QtCore.Qt.MatchCaseSensitive | QtCore.Qt.MatchRecursive,
 
276
                    gui.FOLDER_NAME_COL)
 
277
                self.assertEqual(1, len(items))
 
278
                item = items[0]
230
279
                self.assert_folder_row_correct(item, name, icon_name, volume,
231
280
                                               tweaked_path=expected_path)
232
281
 
233
 
            treeiter += 1
234
 
            item = treeiter.value()
235
 
 
236
282
    def test_process_info_clears_the_list(self):
237
283
        """The old volumes info is cleared before updated."""
238
284
        self.ui.process_info(FAKE_VOLUMES_INFO)
254
300
 
255
301
        child_index = 0
256
302
        root = self.ui.ui.folders.invisibleRootItem()
257
 
        for name, _, _ in FAKE_VOLUMES_NO_FREE_SPACE_INFO:
258
 
            name = _build_name(name)
259
 
 
 
303
        names = [_build_name(name) for name, _, _ in
 
304
            FAKE_VOLUMES_NO_FREE_SPACE_INFO]
 
305
        names.sort(key=lambda x: x.lower())
 
306
        for name in names:
260
307
            item = root.child(child_index)
261
308
            self.assertFalse(item.is_empty)
262
309
            self.assert_folder_group_header_correct(item, name)
363
410
        """Ensure that the inner widgets are in the correct tab order."""
364
411
        self.ui.process_info(FAKE_VOLUMES_INFO)
365
412
        folders = self.ui.ui.folders
366
 
 
367
413
        widget = self.ui.ui.folders.nextInFocusChain()
368
414
        treeiter = gui.QtGui.QTreeWidgetItemIterator(folders)
369
 
        for name, _, volumes in FAKE_VOLUMES_INFO:
370
 
            item = treeiter.value()
371
 
            sorted_vols = sorted(volumes, key=operator.itemgetter('path'))
372
 
            for volume in sorted_vols:
 
415
        while treeiter.value():
 
416
            if treeiter.value().parent() is None:  # Top-levels
373
417
                treeiter += 1
374
 
                item = treeiter.value()  # get child folder
375
 
 
376
 
                name = volume['path'].replace(USER_HOME + os.path.sep, '')
377
 
                if volume['type'] == self.ui.backend.SHARE_TYPE:
378
 
                    name = volume['name']
379
 
                self.assertEqual(unicode(item.text(gui.FOLDER_NAME_COL)), name)
380
 
 
381
 
                if volume['type'] != self.ui.backend.ROOT_TYPE:
382
 
                    self.assertIsInstance(widget, QtGui.QCheckBox)
383
 
                    self.assertEqual(unicode(
384
 
                        self.ui.widget_items[widget].text(0)), name)
385
 
                    widget = widget.nextInFocusChain()
386
 
 
387
 
                if not self.ui.remote_folders:
388
 
                    self.assertIsInstance(widget, QtGui.QPushButton)
389
 
                    self.assertEqual(unicode(
390
 
                        self.ui.widget_items[widget].text(0)), name)
391
 
                    widget = widget.nextInFocusChain()
392
 
 
 
418
                continue
 
419
            if self.ui.remote_folders:
 
420
                self.assertIsInstance(widget, QtGui.QCheckBox)
 
421
            # ROOT volume has no checkbox
 
422
            elif treeiter.value().text(1) or self.ui.remote_folders:
 
423
                self.assertIsInstance(widget, QtGui.QPushButton)
 
424
            else:
 
425
                self.assertIsInstance(widget, QtGui.QCheckBox)
 
426
                widget = widget.nextInFocusChain()
 
427
                self.assertIsInstance(widget, QtGui.QPushButton)
 
428
            widget = widget.nextInFocusChain()
393
429
            treeiter += 1
394
 
            item = treeiter.value()
395
430
 
396
431
    def test_widget_dict(self):
397
432
        """Ensure the widget_items dictionary is full."""
416
451
        self.assertFalse(self.ui.ui.folders.header().isVisible())
417
452
 
418
453
        name = self.ui.ui.folders.headerItem().text(gui.FOLDER_NAME_COL)
419
 
        self.assertEqual(name, gui.FOLDERS_COLUMN_NAME)
 
454
        self.assertEqual(unicode(name), gui.FOLDERS_COLUMN_NAME)
420
455
        name = self.ui.ui.folders.headerItem().text(gui.SUBSCRIPTION_COL)
421
 
        self.assertEqual(name, gui.ALWAYS_SUBSCRIBED)
 
456
        self.assertEqual(unicode(name), gui.ALWAYS_SUBSCRIBED)
422
457
        name = self.ui.ui.folders.headerItem().text(gui.EXPLORE_COL)
423
 
        self.assertEqual(name, gui.FOLDERS_COLUMN_EXPLORE)
 
458
        self.assertEqual(unicode(name), gui.FOLDERS_COLUMN_EXPLORE)
424
459
 
425
460
    def test_share_publish_button(self):
426
461
        """When clicking the share/publish button, the proper url is opened."""
674
709
class BaseLocalFoldersTestCase(BaseTestCase):
675
710
    """Test suite for the class implementing the LocalFolders feature."""
676
711
 
 
712
    fake_thread = True
 
713
 
677
714
    @defer.inlineCallbacks
678
715
    def setUp(self):
679
716
        self.path = u'not-existing-dir'
680
717
        self.expected_size = self.build_test_dir(self.path)
681
718
        self.queue = Queue.Queue()
 
719
        self.kwargs['queue'] = self.queue
 
720
        self.addCleanup(self.kwargs.pop, 'queue')
 
721
 
 
722
        if self.fake_thread:
 
723
            self.patch(gui, 'CalculateSize', FakedCalculateSize)
 
724
 
682
725
        yield super(BaseLocalFoldersTestCase, self).setUp()
683
726
 
684
727
    def build_test_dir(self, dir_path):
715
758
class CalculateSizeTestCase(BaseLocalFoldersTestCase):
716
759
    """Test suite for the CalculateSize thread implementation."""
717
760
 
 
761
    fake_thread = False
 
762
 
718
763
    @defer.inlineCallbacks
719
764
    def setUp(self):
720
765
        yield super(CalculateSizeTestCase, self).setUp()
766
811
        return total_size
767
812
 
768
813
 
769
 
class FakedCalculateSize(object):
770
 
    """A faked CalculateSize thread."""
771
 
 
772
 
    def __init__(self, *args, **kwargs):
773
 
        self.started = False
774
 
 
775
 
    def start(self):
776
 
        """Fake start."""
777
 
        self.started = True
778
 
 
779
 
 
780
814
class CalculateSizeWithInvalidPath(CalculateSizeTestCase):
781
815
    """Test suite for the CalculateSize thread implementation."""
782
816
 
808
842
    @defer.inlineCallbacks
809
843
    def setUp(self):
810
844
        yield super(FolderItemTestCase, self).setUp()
811
 
        self.calculator = FakedCalculateSize(self.path, self.queue)
812
 
        self.patch(gui, 'CalculateSize', lambda *a, **kw: self.calculator)
813
845
        self.values = ['foo', 'bar']
814
846
 
815
 
        assert not self.calculator.started
816
 
 
817
847
    def test_no_params(self):
818
848
        """The creation with no params uses defaults."""
819
849
        item = gui.FolderItem()
820
850
 
821
 
        self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
822
 
        self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
 
851
        self.assertEqual(unicode(item.text(gui.LOCAL_SUBSCRIPTION_COL)), '')
 
852
        self.assertEqual(unicode(item.text(gui.LOCAL_SPACE_COL)), '')
823
853
        self.assertEqual(item.path, None)
824
854
        self.assertEqual(item.volume_id, None)
825
855
        self.assertEqual(item.thread, None)
831
861
        """The creation with only values."""
832
862
        item = gui.FolderItem(values=self.values)
833
863
 
834
 
        self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), self.values[0])
835
 
        self.assertEqual(item.text(gui.LOCAL_SPACE_COL), self.values[1])
 
864
        self.assertEqual(unicode(item.text(gui.LOCAL_SUBSCRIPTION_COL)),
 
865
                         self.values[0])
 
866
        self.assertEqual(unicode(item.text(gui.LOCAL_SPACE_COL)),
 
867
                         self.values[1])
836
868
        self.assertEqual(item.path, None)
837
869
        self.assertEqual(item.volume_id, None)
838
870
        self.assertEqual(item.thread, None)
844
876
        """The creation with only a path."""
845
877
        item = gui.FolderItem(path=self.path)
846
878
 
847
 
        self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
848
 
        self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
 
879
        self.assertEqual(unicode(item.text(gui.LOCAL_SUBSCRIPTION_COL)), '')
 
880
        self.assertEqual(unicode(item.text(gui.LOCAL_SPACE_COL)), '')
849
881
        self.assertEqual(item.path, self.path)
850
882
        self.assertEqual(item.volume_id, None)
851
883
        self.assertEqual(item.thread, None)
857
889
        """The creation with only a queue."""
858
890
        item = gui.FolderItem(queue=self.queue)
859
891
 
860
 
        self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
861
 
        self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
 
892
        self.assertEqual(unicode(item.text(gui.LOCAL_SUBSCRIPTION_COL)), '')
 
893
        self.assertEqual(unicode(item.text(gui.LOCAL_SPACE_COL)), '')
862
894
        self.assertEqual(item.path, None)
863
895
        self.assertEqual(item.volume_id, None)
864
896
        self.assertEqual(item.thread, None)
870
902
        """The creation with only a volume_id."""
871
903
        item = gui.FolderItem(volume_id='yadda')
872
904
 
873
 
        self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
874
 
        self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
 
905
        self.assertEqual(unicode(item.text(gui.LOCAL_SUBSCRIPTION_COL)), '')
 
906
        self.assertEqual(unicode(item.text(gui.LOCAL_SPACE_COL)), '')
875
907
        self.assertEqual(item.path, None)
876
908
        self.assertEqual(item.volume_id, 'yadda')
877
909
        self.assertEqual(item.thread, None)
883
915
        """The creation with only a volume_id."""
884
916
        item = gui.FolderItem(path=self.path, volume_id='yadda')
885
917
 
886
 
        self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
887
 
        self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
 
918
        self.assertEqual(unicode(item.text(gui.LOCAL_SUBSCRIPTION_COL)), '')
 
919
        self.assertEqual(unicode(item.text(gui.LOCAL_SPACE_COL)), '')
888
920
        self.assertEqual(item.path, self.path)
889
921
        self.assertEqual(item.volume_id, 'yadda')
890
922
        self.assertEqual(item.thread, None)
896
928
        """The creation with only a volume_id."""
897
929
        item = gui.FolderItem(path=self.path, queue=self.queue)
898
930
 
899
 
        self.assertEqual(item.text(gui.LOCAL_SUBSCRIPTION_COL), '')
900
 
        self.assertEqual(item.text(gui.LOCAL_SPACE_COL), '')
 
931
        self.assertEqual(unicode(item.text(gui.LOCAL_SUBSCRIPTION_COL)), '')
 
932
        self.assertEqual(unicode(item.text(gui.LOCAL_SPACE_COL)), '')
901
933
        self.assertEqual(item.path, self.path)
902
934
        self.assertEqual(item.volume_id, None)
903
 
        self.assertEqual(item.thread, self.calculator)
 
935
        self.assertIsInstance(item.thread, gui.CalculateSize)
904
936
        self.assertEqual(item.size, None)
905
937
        self.assertEqual(item.checkState(gui.LOCAL_SUBSCRIPTION_COL),
906
938
                         gui.UNCHECKED)
907
 
 
908
 
        self.assertTrue(self.calculator.started)
909
 
 
910
 
 
911
 
class LocalFoldersPanelTestCase(UbuntuOneBinTestCase):
 
939
        # pylint: disable=E1101
 
940
        self.assertTrue(item.thread.started)
 
941
 
 
942
 
 
943
class BaseLocalFoldersPanelTestCase(UbuntuOneBinTestCase,
 
944
                                    BaseLocalFoldersTestCase):
 
945
    """Base test suite for the LocalFoldersPanel widget."""
 
946
 
 
947
    class_ui = gui.LocalFoldersPanel
 
948
 
 
949
    @defer.inlineCallbacks
 
950
    def setUp(self):
 
951
        self.patch(FakedControlPanelBackend, 'exposed_results',
 
952
                   {'volumes_info': [],
 
953
                    'account_info': ACCOUNT_INFO})
 
954
        self.patch(gui, 'default_folders', self.default_folders)
 
955
 
 
956
        self.timer = FakedTimer()
 
957
        self.kwargs['timer'] = self.timer
 
958
        self.addCleanup(self.kwargs.pop, 'timer')
 
959
 
 
960
        yield super(BaseLocalFoldersPanelTestCase, self).setUp()
 
961
 
 
962
        self.patch(self.ui.backend, 'validate_path_for_folder',
 
963
                   self.validate_path)
 
964
 
 
965
    def default_folders(self, user_home):
 
966
        """A fixed default folders list."""
 
967
        return [os.path.join(user_home, f) for f in DEFAULT_FOLDERS]
 
968
 
 
969
    def validate_path(self, folder):
 
970
        """A fixed path validator."""
 
971
        return defer.succeed(u'invalid' not in folder.lower())
 
972
 
 
973
    def assert_space_header_correct(self, total):
 
974
        """Check that the 'used space' header is correct."""
 
975
        total = '(%s)' % gui.humanize(total)
 
976
        expected = gui.LOCAL_FOLDERS_SPACE_HEADER.format(space_total=total)
 
977
        actual = self.ui.ui.folders.headerItem().text(gui.LOCAL_SPACE_COL)
 
978
        self.assertEqual(expected, unicode(actual))
 
979
 
 
980
    def assert_folder_added(self, path, volume_id=None, item=None):
 
981
        """A entry for 'path' was added to the folder list."""
 
982
        folders = self.ui.ui.folders
 
983
 
 
984
        if path == MUSIC_PATH:
 
985
            display_name = gui.MUSIC_DISPLAY_NAME
 
986
        else:
 
987
            display_name = path.replace(USER_HOME + os.path.sep, '')
 
988
        if item is None:  # search for it
 
989
            items = folders.findItems(display_name,
 
990
                QtCore.Qt.MatchFixedString | QtCore.Qt.MatchCaseSensitive,
 
991
                gui.FOLDER_NAME_COL)
 
992
            self.assertEqual(len(items), 1)
 
993
            item = items[0]
 
994
        self.assertEqual(display_name,
 
995
                         unicode(item.text(gui.LOCAL_SUBSCRIPTION_COL)))
 
996
 
 
997
        self.assertEqual(item.path, path)
 
998
        self.assertEqual(item.volume_id, volume_id)
 
999
        # should be checked if the volume_id is not None
 
1000
        subscribed = item.checkState(gui.LOCAL_SUBSCRIPTION_COL) == gui.CHECKED
 
1001
        self.assertEqual(subscribed, volume_id != None)
 
1002
 
 
1003
        self.timer.timeout.emit()  # update sizes will be called
 
1004
 
 
1005
        # if volume_id is not None, no 'space' is expected since is a cloud
 
1006
        # folder, and its size is accounted for in the quota used amount
 
1007
        size = gui.humanize(size_for_path(path)) if volume_id is None else ''
 
1008
        self.assertEqual(unicode(item.text(gui.LOCAL_SPACE_COL)), size)
 
1009
 
 
1010
 
 
1011
class LocalFoldersPanelTestCase(BaseLocalFoldersPanelTestCase):
912
1012
    """Test suite for the LocalFoldersPanel widget."""
913
1013
 
914
 
    class_ui = gui.LocalFoldersPanel
915
 
 
916
 
    # TODO: add the test suite (LP: #959690).
 
1014
    def test_initial_state(self):
 
1015
        """Initial state is correct."""
 
1016
        self.assertIsInstance(self.ui.timer, gui.QtCore.QTimer)
 
1017
        self.assertFalse(self.ui.timer.isActive())
 
1018
 
 
1019
        self.assertEqual(self.ui.ui.folders.topLevelItemCount(), 0)
 
1020
        self.assertFalse(self.ui.ui.offer_frame.isVisible())
 
1021
 
 
1022
        headers = self.ui.ui.folders.header()
 
1023
        self.assertEqual(headers.resizeMode(gui.LOCAL_SUBSCRIPTION_COL),
 
1024
                         headers.Stretch)
 
1025
        self.assertEqual(headers.resizeMode(gui.LOCAL_SPACE_COL),
 
1026
                         headers.ResizeToContents)
 
1027
 
 
1028
        header = self.ui.ui.folders.headerItem()
 
1029
        self.assertEqual(unicode(header.text(gui.LOCAL_SUBSCRIPTION_COL)),
 
1030
                         gui.LOCAL_FOLDERS_FOLDER_HEADER)
 
1031
 
 
1032
        self.assertEqual(unicode(self.ui.ui.add_folder_button.text()),
 
1033
                         gui.FOLDERS_BUTTON_ADD_FOLDER)
 
1034
        self.assertEqual(self.ui.ui.add_folder_button.add_folder_func,
 
1035
                         self.ui.add_folder)
 
1036
        self.assertEqual(unicode(self.ui.ui.add_storage_button.text()),
 
1037
                         gui.GET_MORE_STORAGE)
 
1038
 
 
1039
    @defer.inlineCallbacks
 
1040
    def test_is_processing_while_asking_info(self):
 
1041
        """The ui is processing while the contents are loaded."""
 
1042
 
 
1043
        def check_account():
 
1044
            """The ui must be is_processing."""
 
1045
            self.assertTrue(self.ui.is_processing, 'ui must be processing')
 
1046
            return ACCOUNT_INFO
 
1047
 
 
1048
        def check_volumes(with_storage_info):
 
1049
            """The ui must be is_processing."""
 
1050
            self.assertFalse(with_storage_info)
 
1051
            self.assertTrue(self.ui.is_processing, 'ui must be processing')
 
1052
            return FAKE_VOLUMES_ONLY_ROOT_INFO
 
1053
 
 
1054
        self.patch(self.ui.backend, 'account_info', check_account)
 
1055
        self.patch(self.ui.backend, 'volumes_info', check_volumes)
 
1056
 
 
1057
        yield self.ui.load()  # trigger the info request
 
1058
 
 
1059
        self.assertEqual(self.ui.account_info, ACCOUNT_INFO)
 
1060
        self.assert_space_header_correct(ACCOUNT_INFO['quota_used'])
 
1061
 
 
1062
    def test_is_not_processing_after_info_ready(self):
 
1063
        """The ui is not processing when contents are load."""
 
1064
        self.ui.process_info(FAKE_VOLUMES_ONLY_ROOT_INFO)
 
1065
 
 
1066
        self.assertFalse(self.ui.is_processing)
 
1067
 
 
1068
    @defer.inlineCallbacks
 
1069
    def test_info_is_requested_on_load(self):
 
1070
        """The volumes info is requested to the backend."""
 
1071
        yield self.ui.load()
 
1072
        self.assert_backend_called('account_info')
 
1073
        self.assert_backend_called('volumes_info', with_storage_info=False)
 
1074
 
 
1075
    @defer.inlineCallbacks
 
1076
    def test_process_info(self):
 
1077
        """The volumes info is processed when ready."""
 
1078
        volumes_info = FAKE_VOLUMES_INFO
 
1079
        yield self.ui.process_info(volumes_info)
 
1080
 
 
1081
        self.assertTrue(self.ui.timer.isActive())
 
1082
 
 
1083
        # the following will execute update_sizes once
 
1084
        # will fill in all the path sizes for the already added folders
 
1085
        self.timer.timeout.emit()
 
1086
        volumes = []
 
1087
        for _, _, vols in volumes_info:
 
1088
            volumes.extend(vols)
 
1089
        volumes = sorted(volumes, key=operator.itemgetter('path'))
 
1090
 
 
1091
        for volume in volumes:
 
1092
            if (volume['type'] != self.ui.backend.FOLDER_TYPE or
 
1093
                not bool(volume['subscribed'])):
 
1094
                # non-folders or unsubscribed should not appear in the tree
 
1095
                continue
 
1096
            self.assert_folder_added(volume['path'],
 
1097
                                     volume_id=volume['volume_id'])
 
1098
        for folder in gui.default_folders(USER_HOME):
 
1099
            valid = yield self.validate_path(folder)
 
1100
            if not valid:
 
1101
                continue  # invalid folders should not appear in the tree
 
1102
            self.assert_folder_added(folder, volume_id=None, item=None)
 
1103
 
 
1104
        self.assert_space_header_correct(ACCOUNT_INFO['quota_used'])
 
1105
 
 
1106
    @defer.inlineCallbacks
 
1107
    def test_process_info_twice(self):
 
1108
        """The volumes info can be processed twice."""
 
1109
        yield self.test_process_info()
 
1110
        yield self.test_process_info()
 
1111
 
 
1112
    @defer.inlineCallbacks
 
1113
    def test_timer_is_not_started_if_no_volumes(self):
 
1114
        """If not volumes to show, timer is not started."""
 
1115
        self.patch(gui, 'default_folders', lambda *a, **kw: [])
 
1116
        yield self.ui.process_info([])
 
1117
        self.assertFalse(self.ui.timer.isActive())
 
1118
 
 
1119
    @defer.inlineCallbacks
 
1120
    def test_on_folders_item_changed_subscription_column(self):
 
1121
        """If a new folder is subscribed, update header size."""
 
1122
        # processing only a root folder will force showing only default,
 
1123
        # unsubscribed folders
 
1124
        yield self.ui.process_info(FAKE_VOLUMES_ONLY_ROOT_INFO)
 
1125
 
 
1126
        new_size = ACCOUNT_INFO['quota_used']
 
1127
        for i in xrange(self.ui.ui.folders.topLevelItemCount()):
 
1128
            item = self.ui.ui.folders.topLevelItem(i)
 
1129
            item.setCheckState(gui.LOCAL_SUBSCRIPTION_COL, gui.CHECKED)
 
1130
            new_size += size_for_path(item.path)
 
1131
            self.assert_space_header_correct(new_size)
 
1132
 
 
1133
        # quota was not filled, so "get more storage" is not shown
 
1134
        available = ACCOUNT_INFO['quota_total'] - ACCOUNT_INFO['quota_used']
 
1135
        assert new_size <= available
 
1136
        self.assertFalse(self.ui.ui.offer_frame.isVisible())
 
1137
 
 
1138
        # now, deselect each folder and confirm space column is correct
 
1139
        for i in xrange(self.ui.ui.folders.topLevelItemCount()):
 
1140
            item = self.ui.ui.folders.topLevelItem(i)
 
1141
            item.setCheckState(gui.LOCAL_SUBSCRIPTION_COL, gui.UNCHECKED)
 
1142
            new_size -= size_for_path(item.path)
 
1143
            self.assert_space_header_correct(new_size)
 
1144
 
 
1145
        self.assert_space_header_correct(ACCOUNT_INFO['quota_used'])
 
1146
 
 
1147
    @defer.inlineCallbacks
 
1148
    def test_on_folders_item_changed_unsubscribing(self):
 
1149
        """If a cloud folder was unsubscribed, do not update header."""
 
1150
        # patch update_sizes with sel.fail to ensure is not called
 
1151
        self.patch(self.ui, 'update_sizes', self.fail)
 
1152
 
 
1153
        # process only root and a subscribed music folder
 
1154
        yield self.ui.process_info(FAKE_VOLUMES_MINIMAL_INFO)
 
1155
 
 
1156
        item = self.ui.ui.folders.topLevelItem(0)
 
1157
        item.setCheckState(gui.LOCAL_SUBSCRIPTION_COL, gui.UNCHECKED)
 
1158
 
 
1159
    @defer.inlineCallbacks
 
1160
    def test_on_folders_item_changed_space_column(self):
 
1161
        """If user clicked the 'space' column, don't update sizes."""
 
1162
        # patch update_sizes with sel.fail to ensure is not called
 
1163
        self.patch(self.ui, 'update_sizes', self.fail)
 
1164
 
 
1165
        yield self.ui.process_info(FAKE_VOLUMES_INFO)
 
1166
 
 
1167
        for i in xrange(self.ui.ui.folders.topLevelItemCount()):
 
1168
            item = self.ui.ui.folders.topLevelItem(i)
 
1169
            self.ui.on_folders_itemChanged(item, gui.LOCAL_SPACE_COL)
 
1170
            # size does not change
 
1171
            self.assert_space_header_correct(ACCOUNT_INFO['quota_used'])
 
1172
 
 
1173
    @defer.inlineCallbacks
 
1174
    def test_going_over_quota(self):
 
1175
        """When subscribing a folder, alert the user if quota was exceeded."""
 
1176
        yield self.ui.process_info(FAKE_VOLUMES_INFO)
 
1177
 
 
1178
        available = ACCOUNT_INFO['quota_total'] - ACCOUNT_INFO['quota_used']
 
1179
        self.patch(FakedCalculateSize, 'next_size', available + 1)
 
1180
 
 
1181
        self.ui.add_folder(self.path)
 
1182
        self.ui.on_folder_created(self.path)
 
1183
 
 
1184
        # quota was exceeeded, so "get more storage" should be shown
 
1185
        self.assert_space_header_correct(ACCOUNT_INFO['quota_total'] + 1)
 
1186
        self.assertTrue(self.ui.ui.offer_frame.isVisible())
 
1187
 
 
1188
    # XXX: still pending to add tests about stopping the timer and the threads
 
1189
 
 
1190
 
 
1191
class LocalFoldersPanelAddFolderTestCase(BaseLocalFoldersPanelTestCase):
 
1192
    """Test suite for the add_folder method from LocalFoldersPanel."""
 
1193
 
 
1194
    def test_folder_is_added(self, folder_path=None, suggested=None):
 
1195
        """Adding a folder adds it to the folder list."""
 
1196
        former_count = self.ui.ui.folders.topLevelItemCount()
 
1197
 
 
1198
        if folder_path is None:
 
1199
            folder_path = self.path
 
1200
        self.ui.add_folder(folder_path, suggested=suggested)
 
1201
 
 
1202
        new_count = self.ui.ui.folders.topLevelItemCount()
 
1203
        self.assertEqual(new_count, former_count + 1)
 
1204
        self.assert_folder_added(folder_path)
 
1205
 
 
1206
    def test_add_music_folder_adds_the_folder(self):
 
1207
        """Adding the purchased music folder adds it to the folder list."""
 
1208
        self.test_folder_is_added(folder_path=MUSIC_PATH,
 
1209
            suggested=MUSIC_REAL_PATH)
 
1210
 
 
1211
    def test_add_music_folder_adds_the_folder_with_suggested_path(self):
 
1212
        """Adding the purchased music folder adds it to the folder list."""
 
1213
        suggested_path = '~/' + MUSIC_REAL_PATH
 
1214
        self.test_folder_is_added(folder_path=MUSIC_PATH,
 
1215
            suggested=suggested_path)
 
1216
 
 
1217
    def test_do_not_add_twice(self):
 
1218
        """The same folder will not be added twice."""
 
1219
        assert self.ui.ui.folders.topLevelItemCount() == 0
 
1220
 
 
1221
        self.ui.add_folder(self.path)
 
1222
        self.ui.add_folder(self.path)
 
1223
 
 
1224
        self.assert_folder_added(self.path)
 
1225
        self.assertEqual(1, self.ui.ui.folders.topLevelItemCount())
 
1226
 
 
1227
    def test_do_not_call_backend(self):
 
1228
        """Adding a folder do not call the backend."""
 
1229
        self.ui.backend._called.clear()
 
1230
        self.ui.ui.add_folder_button.folderCreated.emit(self.path)
 
1231
 
 
1232
        self.assertEqual(self.ui.backend._called, {})
 
1233
 
 
1234
    def test_on_folder_created(self):
 
1235
        """When a folder was created, subscribe to it."""
 
1236
        self.ui.add_folder(self.path)
 
1237
 
 
1238
        folders = self.ui.ui.folders
 
1239
        item = folders.topLevelItem(folders.topLevelItemCount() - 1)
 
1240
        assert item.checkState(gui.LOCAL_SUBSCRIPTION_COL) == gui.UNCHECKED
 
1241
 
 
1242
        self.ui.ui.add_folder_button.folderCreated.emit(self.path)
 
1243
 
 
1244
        subscribed = item.checkState(gui.LOCAL_SUBSCRIPTION_COL) == gui.CHECKED
 
1245
        self.assertTrue(subscribed)
 
1246
 
 
1247
 
 
1248
class LocalFoldersPanelApplyChangesTestCase(BaseLocalFoldersPanelTestCase):
 
1249
    """Test suite for the apply_changes method from LocalFoldersPanel."""
 
1250
 
 
1251
    @defer.inlineCallbacks
 
1252
    def setUp(self):
 
1253
        yield super(LocalFoldersPanelApplyChangesTestCase, self).setUp()
 
1254
        self.changes_applied = defer.Deferred()
 
1255
        self.ui.backend._called.clear()
 
1256
        self.ui.changesApplied.connect(lambda:
 
1257
                                       self.changes_applied.callback(None))
 
1258
 
 
1259
    @defer.inlineCallbacks
 
1260
    def test_apply_changes_calls_backend(self):
 
1261
        """When applying changes, the calls to the backend are made."""
 
1262
        yield self.ui.apply_changes()
 
1263
        yield self.changes_applied
 
1264
 
 
1265
    @defer.inlineCallbacks
 
1266
    def test_apply_changes_stop_threads(self):
 
1267
        """When applying changes, threads are stopped."""
 
1268
        yield self.ui.apply_changes()
 
1269
        yield self.changes_applied
 
1270
 
 
1271
    @defer.inlineCallbacks
 
1272
    def test_apply_changes_with_no_changes(self):
 
1273
        """If there are no changes to apply, the backend is not called."""
 
1274
        yield self.ui.apply_changes()
 
1275
        yield self.changes_applied
 
1276
        self.assertEqual(self.ui.backend._called, {})
 
1277
 
 
1278
    def test_timer_is_stopped(self):
 
1279
        """When applying changes, timer is stopped."""
 
1280
        yield self.ui.apply_changes()
 
1281
        yield self.changes_applied
 
1282
        self.assertFalse(self.ui.timer.isActive())
 
1283
 
 
1284
    # XXX: more tests are needed, to cover that only those newly added folders,
 
1285
    # and those newly subscribed or unsibscribed folders are modified by
 
1286
    # calling the backend by either create_folder or change_volume_settings