~nataliabidart/magicicada-client/simpler-setup-py

« back to all changes in this revision

Viewing changes to ubuntuone/status/tests/test_aggregator.py

  • Committer: Magicicada Bot
  • Author(s): Facundo Batista
  • Date: 2016-09-17 15:51:03 UTC
  • mfrom: (1429.1.2 xenial-tests)
  • Revision ID: magicicada_bot-20160917155103-nmjc0bpobcndy537
[r=nataliabidart] Brought tests (almost, see description) back to life.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# tests.status.test_aggregator
2
 
#
3
 
# Author: Alejandro J. Cura <alecu@canonical.com>
4
 
#
5
 
# Copyright 2011-2012 Canonical Ltd.
6
 
#
7
 
# This program is free software: you can redistribute it and/or modify it
8
 
# under the terms of the GNU General Public License version 3, as published
9
 
# by the Free Software Foundation.
10
 
#
11
 
# This program is distributed in the hope that it will be useful, but
12
 
# WITHOUT ANY WARRANTY; without even the implied warranties of
13
 
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14
 
# PURPOSE.  See the GNU General Public License for more details.
15
 
#
16
 
# You should have received a copy of the GNU General Public License along
17
 
# with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 
#
19
 
# In addition, as a special exception, the copyright holders give
20
 
# permission to link the code of portions of this program with the
21
 
# OpenSSL library under certain conditions as described in each
22
 
# individual source file, and distribute linked combinations
23
 
# including the two.
24
 
# You must obey the GNU General Public License in all respects
25
 
# for all of the code used other than OpenSSL.  If you modify
26
 
# file(s) with this exception, you may extend this exception to your
27
 
# version of the file(s), but you are not obligated to do so.  If you
28
 
# do not wish to do so, delete this exception statement from your
29
 
# version.  If you delete this exception statement from all source
30
 
# files in the program, then also delete it here.
31
 
"""Tests for the status events aggregator."""
32
 
 
33
 
import logging
34
 
 
35
 
from twisted.internet import defer
36
 
from twisted.internet.task import Clock
37
 
from twisted.trial.unittest import TestCase
38
 
from mocker import Mocker
39
 
 
40
 
from contrib.testing.testcase import BaseTwistedTestCase
41
 
from ubuntuone.devtools.handlers import MementoHandler
42
 
from ubuntuone.devtools.testcases import skipTest
43
 
from ubuntuone.status import aggregator
44
 
from ubuntuone.status.notification import AbstractNotification
45
 
from ubuntuone.syncdaemon import (
46
 
    status_listener,
47
 
    RECENT_TRANSFERS,
48
 
    UPLOADING,
49
 
    DOWNLOADING
50
 
)
51
 
from ubuntuone.syncdaemon.volume_manager import Share, UDF, Root
52
 
 
53
 
FILENAME = 'example.txt'
54
 
FILENAME2 = 'another_example.mp3'
55
 
 
56
 
 
57
 
class PatchedClock(Clock):
58
 
    """Patch the clock to fix twisted bug #4823."""
59
 
 
60
 
    def advance(self, amount):
61
 
        """Sort the calls before advancing the clock."""
62
 
        self.calls.sort(lambda a, b: cmp(a.getTime(), b.getTime()))
63
 
        Clock.advance(self, amount)
64
 
 
65
 
 
66
 
class TimerTestCase(TestCase):
67
 
    """Test the Timer class."""
68
 
 
69
 
    TIMEOUT = 3.0
70
 
 
71
 
    @defer.inlineCallbacks
72
 
    def setUp(self):
73
 
        """Initialize this test instance."""
74
 
        yield super(TimerTestCase, self).setUp()
75
 
        self.clock = PatchedClock()
76
 
        self.timer = aggregator.Timer(delay=3.0, clock=self.clock)
77
 
 
78
 
    def test_not_fired_initially(self):
79
 
        """The timer is not fired initially"""
80
 
        self.assertFalse(self.timer.called)
81
 
 
82
 
    def test_fired_after_delay(self):
83
 
        """The timer is fired after the initial delay."""
84
 
        self.clock.advance(self.timer.delay)
85
 
        self.assertTrue(self.timer.called)
86
 
 
87
 
    def test_cleanup_cancels_delay_call(self):
88
 
        """Calling cleanup cancels the delay call."""
89
 
        self.timer.cleanup()
90
 
        self.assertTrue(self.timer.delay_call.cancelled)
91
 
 
92
 
    def test_not_fired_immediately(self):
93
 
        """The timer is not fired immediately."""
94
 
        self.timer.reset()
95
 
        self.assertFalse(self.timer.called)
96
 
 
97
 
    def test_fired_after_initial_wait(self):
98
 
        """The timer is fired after an initial wait."""
99
 
        self.timer.reset()
100
 
        self.clock.advance(self.timer.delay)
101
 
        self.assertTrue(self.timer.called)
102
 
 
103
 
    def test_not_fired_if_reset_within_delay(self):
104
 
        """The timer is not fired if it is reset within the delay."""
105
 
        self.timer.reset()
106
 
        self.clock.advance(self.timer.delay / 0.8)
107
 
        self.timer.reset()
108
 
        self.clock.advance(self.timer.delay / 0.8)
109
 
        self.assertTrue(self.timer.called)
110
 
 
111
 
    def test_active(self):
112
 
        """The timer is active until the delay is reached."""
113
 
        self.timer.reset()
114
 
        self.assertTrue(self.timer.active)
115
 
        self.clock.advance(self.timer.delay + 1)
116
 
        self.assertFalse(self.timer.active)
117
 
 
118
 
 
119
 
class DeadlineTimerTestCase(TimerTestCase):
120
 
    """Test the DeadlineTimer class."""
121
 
 
122
 
    DELAY = 0.5
123
 
 
124
 
    @defer.inlineCallbacks
125
 
    def setUp(self):
126
 
        """Initialize this test instance."""
127
 
        yield super(DeadlineTimerTestCase, self).setUp()
128
 
        self.clock = PatchedClock()
129
 
        self.timer = aggregator.DeadlineTimer(
130
 
            delay=0.5, timeout=3.0, clock=self.clock)
131
 
 
132
 
    def test_fired_if_initial_timeout_exceeded(self):
133
 
        """Timer is fired if the initial timeout is exceeded."""
134
 
        small_delay = self.timer.delay * 0.8
135
 
        for n in range(int(self.timer.timeout / small_delay) + 1):
136
 
            self.timer.reset()
137
 
            self.clock.advance(small_delay)
138
 
        self.assertTrue(self.timer.called)
139
 
 
140
 
    def test_not_fired_twice_if_delay_exceeded(self):
141
 
        """Timer is not fired twice if the delay is exceeded."""
142
 
        large_delay = self.timer.delay * 1.2
143
 
        for n in range(int(self.timer.timeout / large_delay) + 1):
144
 
            self.timer.reset()
145
 
            self.clock.advance(large_delay)
146
 
        self.clock.advance(self.timer.delay)
147
 
        self.assertTrue(self.timer.called)
148
 
 
149
 
    def test_not_fired_twice_if_timeout_exceeded(self):
150
 
        """Timer is not fired twice if the timeout is exceeded."""
151
 
        small_delay = self.timer.delay * 0.8
152
 
        for n in range(int(self.timer.timeout / small_delay) + 1):
153
 
            self.timer.reset()
154
 
            self.clock.advance(small_delay)
155
 
        self.clock.advance(self.timer.delay)
156
 
        self.assertTrue(self.timer.called)
157
 
 
158
 
    def test_cleanup_cancels_timeout_call(self):
159
 
        """Calling cleanup cancels the delay call."""
160
 
        self.timer.cleanup()
161
 
        self.assertTrue(self.timer.timeout_call.cancelled)
162
 
 
163
 
 
164
 
class FakeNotification(AbstractNotification):
165
 
    """A fake notification class."""
166
 
 
167
 
    def __init__(self, application_name="fake app"):
168
 
        """Initialize this instance."""
169
 
        self.notifications_shown = []
170
 
        self.notification_switch = None
171
 
        self.application_name = application_name
172
 
        self.notification = None
173
 
 
174
 
    def send_notification(self, title, message, icon=None, append=False):
175
 
        """Show a notification to the user."""
176
 
        if (self.notification_switch is not None and
177
 
                not self.notification_switch.enabled):
178
 
            return
179
 
        self.notification = (title, message, icon, append)
180
 
        self.notifications_shown.append((title, message, icon, append))
181
 
        return len(self.notifications_shown) - 1
182
 
 
183
 
 
184
 
def FakeNotificationSingleton():
185
 
    """Builds a notification singleton, that logs all notifications shown."""
186
 
    instance = FakeNotification()
187
 
 
188
 
    def get_instance(notification_switch):
189
 
        """Returns the single instance."""
190
 
        instance.notification_switch = notification_switch
191
 
        return instance
192
 
 
193
 
    return get_instance
194
 
 
195
 
 
196
 
class FakeStatusAggregator(object):
197
 
    """A fake status aggregator."""
198
 
 
199
 
    def __init__(self, clock):
200
 
        """Initialize this instance."""
201
 
        self.discovered = 0
202
 
        self.completed = 0
203
 
        self.notification_switch = aggregator.NotificationSwitch()
204
 
 
205
 
    def get_discovery_message(self):
206
 
        """Return the file discovery message."""
207
 
        self.discovered += 1
208
 
        return self.build_discovery_message()
209
 
 
210
 
    def build_discovery_message(self):
211
 
        """Build the file discovery message."""
212
 
        return "a lot of files found (%d)." % self.discovered
213
 
 
214
 
    def get_progress_message(self):
215
 
        """Return the progress message."""
216
 
        self.completed += 1
217
 
        return self.build_progress_message()
218
 
 
219
 
    def build_progress_message(self):
220
 
        """Build the progress message."""
221
 
        params = (self.discovered, self.completed)
222
 
        return "a lot of files transferring (%d/%d)." % params
223
 
 
224
 
    def get_final_status_message(self):
225
 
        """Return the final status message."""
226
 
        return "a lot of files completed."
227
 
 
228
 
    def get_notification(self):
229
 
        """Create a new toggleable notification object."""
230
 
        return self.notification_switch.get_notification()
231
 
 
232
 
 
233
 
class ToggleableNotificationTestCase(TestCase):
234
 
    """Test the ToggleableNotification class."""
235
 
 
236
 
    @defer.inlineCallbacks
237
 
    def setUp(self):
238
 
        """Initialize this test instance."""
239
 
        yield super(ToggleableNotificationTestCase, self).setUp()
240
 
        self.patch(aggregator.notification, "Notification", FakeNotification)
241
 
        self.notification_switch = aggregator.NotificationSwitch()
242
 
        self.toggleable = self.notification_switch.get_notification()
243
 
 
244
 
    def assertShown(self, notification):
245
 
        """Assert that the notification was shown."""
246
 
        self.assertIn(notification,
247
 
                      self.toggleable.notification.notifications_shown)
248
 
 
249
 
    def assertNotShown(self, notification):
250
 
        """Assert that the notification was shown."""
251
 
        self.assertNotIn(notification,
252
 
                         self.toggleable.notification.notifications_shown)
253
 
 
254
 
    def test_send_notification_passes_thru(self):
255
 
        """The send_notification method passes thru."""
256
 
        args = (1, 2, 3, 4)
257
 
        self.toggleable.send_notification(*args)
258
 
        self.assertShown(args)
259
 
 
260
 
    def test_send_notification_honored_when_enabled(self):
261
 
        """The send_notification method is honored when enabled."""
262
 
        self.notification_switch.enable_notifications()
263
 
        args = (aggregator.NAME, "hello", None, False)
264
 
        self.toggleable.send_notification(*args)
265
 
        self.assertShown(args)
266
 
 
267
 
    def test_send_notification_ignored_when_disabled(self):
268
 
        """The send_notification method is ignored when disabled."""
269
 
        self.notification_switch.disable_notifications()
270
 
        args = (aggregator.NAME, "hello", None, False)
271
 
        self.toggleable.send_notification(*args)
272
 
        self.assertNotShown(args)
273
 
 
274
 
 
275
 
class NotificationSwitchTestCase(TestCase):
276
 
    """Test the NotificationSwitch class."""
277
 
 
278
 
    @defer.inlineCallbacks
279
 
    def setUp(self):
280
 
        """Initialize this test instance."""
281
 
        yield super(NotificationSwitchTestCase, self).setUp()
282
 
        self.notification_switch = aggregator.NotificationSwitch()
283
 
 
284
 
    def test_get_notification(self):
285
 
        """A new notification instance is returned."""
286
 
        notification = self.notification_switch.get_notification()
287
 
        self.assertEqual(notification.notification_switch,
288
 
                         self.notification_switch)
289
 
 
290
 
    def test_enable_notifications(self):
291
 
        """The switch is turned on."""
292
 
        self.notification_switch.enable_notifications()
293
 
        self.assertTrue(self.notification_switch.enabled)
294
 
 
295
 
    def test_disable_notifications(self):
296
 
        """The switch is turned off."""
297
 
        self.notification_switch.disable_notifications()
298
 
        self.assertFalse(self.notification_switch.enabled)
299
 
 
300
 
 
301
 
class FileDiscoveryBubbleTestCase(TestCase):
302
 
    """Test the FileDiscoveryBubble class."""
303
 
 
304
 
    @defer.inlineCallbacks
305
 
    def setUp(self):
306
 
        """Initialize this test instance."""
307
 
        yield super(FileDiscoveryBubbleTestCase, self).setUp()
308
 
        self.patch(aggregator, "ToggleableNotification",
309
 
                   FakeNotificationSingleton())
310
 
        self.clock = PatchedClock()
311
 
        self.aggregator = FakeStatusAggregator(clock=self.clock)
312
 
        self.bubble = aggregator.FileDiscoveryBubble(self.aggregator,
313
 
                                                     clock=self.clock)
314
 
        self.addCleanup(self.bubble.cleanup)
315
 
        fdis = aggregator.FileDiscoveryGatheringState
316
 
        self.initial_delay = fdis.initial_delay
317
 
        self.smaller_delay = self.initial_delay * 0.8
318
 
        self.initial_timeout = fdis.initial_timeout
319
 
        fdus = aggregator.FileDiscoveryUpdateState
320
 
        self.updates_delay = fdus.updates_delay
321
 
        self.updates_timeout = fdus.updates_timeout
322
 
        fdss = aggregator.FileDiscoverySleepState
323
 
        self.sleep_delay = fdss.sleep_delay
324
 
 
325
 
        self.handler = MementoHandler()
326
 
        self.handler.setLevel(logging.DEBUG)
327
 
        aggregator.logger.addHandler(self.handler)
328
 
        aggregator.logger.setLevel(logging.DEBUG)
329
 
        self.addCleanup(aggregator.logger.removeHandler, self.handler)
330
 
 
331
 
    def get_notifications_shown(self):
332
 
        """The list of notifications shown."""
333
 
        return self.bubble.notification.notifications_shown
334
 
 
335
 
    def test_popup_shows_notification_when_connected(self):
336
 
        """The popup callback shows notifications."""
337
 
        self.bubble.connection_made()
338
 
        self.bubble.new_file_found()
339
 
        self.bubble._popup()
340
 
        message = self.aggregator.build_discovery_message()
341
 
        notification = (aggregator.NAME, message, None, False)
342
 
        self.assertIn(notification, self.get_notifications_shown())
343
 
 
344
 
    def test_popup_shows_notification_after_connected(self):
345
 
        """The popup callback shows notifications."""
346
 
        self.bubble.new_file_found()
347
 
        self.bubble.connection_made()
348
 
        message = self.aggregator.build_discovery_message()
349
 
        notification = (aggregator.NAME, message, None, False)
350
 
        self.assertIn(notification, self.get_notifications_shown())
351
 
 
352
 
    def test_popup_shows_no_notification_before_connection_made(self):
353
 
        """The popup callback shows notifications."""
354
 
        self.bubble.new_file_found()
355
 
        self.bubble._popup()
356
 
        message = self.aggregator.build_discovery_message()
357
 
        notification = (aggregator.NAME, message, None, False)
358
 
        self.assertNotIn(notification, self.get_notifications_shown())
359
 
 
360
 
    def test_popup_shows_no_notification_after_connection_lost(self):
361
 
        """The popup callback shows notifications."""
362
 
        self.bubble.connection_made()
363
 
        self.bubble.connection_lost()
364
 
        self.bubble.new_file_found()
365
 
        self.bubble._popup()
366
 
        message = self.aggregator.build_discovery_message()
367
 
        notification = (aggregator.NAME, message, None, False)
368
 
        self.assertNotIn(notification, self.get_notifications_shown())
369
 
 
370
 
    def test_notification_is_logged_in_debug(self):
371
 
        """The notification is printed in the debug log."""
372
 
        self.bubble.connection_made()
373
 
        self.bubble.new_file_found()
374
 
        self.bubble._popup()
375
 
        msg = "notification shown: %s" % self.get_notifications_shown()[0][1]
376
 
        self.assertTrue(self.handler.check_debug(msg))
377
 
 
378
 
    def test_bubble_is_not_shown_initially(self):
379
 
        """The bubble is not shown initially."""
380
 
        self.bubble.new_file_found()
381
 
        self.assertEqual(0, len(self.get_notifications_shown()))
382
 
 
383
 
    def test_bubble_is_shown_after_delay(self):
384
 
        """The bubble is shown after a delay."""
385
 
        self.bubble.connection_made()
386
 
        self.bubble.new_file_found()
387
 
        self.clock.advance(self.initial_delay)
388
 
        self.assertEqual(1, len(self.get_notifications_shown()))
389
 
 
390
 
    def test_bubble_not_shown_if_more_files_found(self):
391
 
        """The bubble is not shown if more files found within delay."""
392
 
        self.clock.advance(self.smaller_delay)
393
 
        self.bubble.new_file_found()
394
 
        self.clock.advance(self.smaller_delay)
395
 
        self.assertEqual(0, len(self.get_notifications_shown()))
396
 
 
397
 
    def test_bubble_shown_if_timeout_exceeded(self):
398
 
        """The bubble is shown if the timeout is exceeded."""
399
 
        self.bubble.connection_made()
400
 
        self.bubble.new_file_found()
401
 
        count = int(self.initial_timeout / self.smaller_delay) + 1
402
 
        for n in range(count):
403
 
            self.clock.advance(self.smaller_delay)
404
 
            self.bubble.new_file_found()
405
 
        self.assertEqual(1, len(self.get_notifications_shown()))
406
 
 
407
 
    def test_idle_state(self):
408
 
        """The idle state is verified."""
409
 
        self.assertEqual(
410
 
            type(self.bubble.state), aggregator.FileDiscoveryIdleState)
411
 
 
412
 
    def test_gathering_state(self):
413
 
        """The gathering state is set after the first file is found."""
414
 
        self.bubble.new_file_found()
415
 
        self.assertEqual(
416
 
            type(self.bubble.state), aggregator.FileDiscoveryGatheringState)
417
 
 
418
 
    def test_update_state(self):
419
 
        """When the gathering state finishes, the update state is started."""
420
 
        self.bubble.connection_made()
421
 
        self.bubble.new_file_found()
422
 
        self.clock.advance(self.initial_delay)
423
 
        self.assertEqual(
424
 
            type(self.bubble.state), aggregator.FileDiscoveryUpdateState)
425
 
 
426
 
    def test_sleeping_state(self):
427
 
        """When the update state finishes, the sleeping state is started."""
428
 
        self.bubble.connection_made()
429
 
        self.bubble.new_file_found()
430
 
        self.clock.advance(self.initial_delay)
431
 
        self.clock.advance(self.updates_timeout)
432
 
        self.assertEqual(
433
 
            type(self.bubble.state), aggregator.FileDiscoverySleepState)
434
 
 
435
 
    def test_back_to_initial_state(self):
436
 
        """When the last state finishes, we return to the idle state."""
437
 
        self.bubble.connection_made()
438
 
        self.bubble.new_file_found()
439
 
        self.clock.advance(self.initial_delay)
440
 
        self.clock.advance(self.updates_timeout)
441
 
        self.clock.advance(self.sleep_delay)
442
 
        self.assertEqual(
443
 
            type(self.bubble.state), aggregator.FileDiscoveryIdleState)
444
 
 
445
 
    def test_new_files_found_while_updating_not_shown_immediately(self):
446
 
        """New files found in the updating state are not shown immediately."""
447
 
        self.bubble.connection_made()
448
 
        self.bubble.new_file_found()
449
 
        self.clock.advance(self.initial_delay)
450
 
        self.bubble.new_file_found()
451
 
        self.assertEqual(1, len(self.get_notifications_shown()))
452
 
 
453
 
    def test_new_files_found_while_updating_are_shown_after_a_delay(self):
454
 
        """New files found in the updating state are shown after a delay."""
455
 
        self.bubble.connection_made()
456
 
        self.bubble.new_file_found()
457
 
        self.clock.advance(self.initial_delay)
458
 
        self.bubble.new_file_found()
459
 
        self.clock.advance(self.updates_delay)
460
 
        self.assertEqual(2, len(self.get_notifications_shown()))
461
 
 
462
 
    def test_update_modifies_notification(self):
463
 
        """The update callback updates notifications."""
464
 
        self.bubble.connection_made()
465
 
        self.bubble.new_file_found()
466
 
        self.bubble._popup()
467
 
        self.bubble.new_file_found()
468
 
        self.bubble._update()
469
 
        message = self.aggregator.build_discovery_message()
470
 
        notification = (aggregator.NAME, message, None, False)
471
 
        self.assertIn(notification, self.get_notifications_shown())
472
 
 
473
 
    def test_update_is_logged_in_debug(self):
474
 
        """The notification is logged when _update is called."""
475
 
        self.bubble.connection_made()
476
 
        self.bubble.new_file_found()
477
 
        self.bubble._popup()
478
 
        self.bubble.new_file_found()
479
 
        self.bubble._update()
480
 
        msg = "notification updated: %s" % self.get_notifications_shown()[1][1]
481
 
        self.assertTrue(self.handler.check_debug(msg))
482
 
 
483
 
 
484
 
class FinalBubbleTestCase(TestCase):
485
 
    """Test for the final status notification bubble."""
486
 
 
487
 
    @defer.inlineCallbacks
488
 
    def setUp(self):
489
 
        """Initialize this test instance."""
490
 
        yield super(FinalBubbleTestCase, self).setUp()
491
 
        self.patch(aggregator, "ToggleableNotification",
492
 
                   FakeNotificationSingleton())
493
 
        self.clock = PatchedClock()
494
 
        self.aggregator = FakeStatusAggregator(clock=self.clock)
495
 
        self.bubble = aggregator.FinalStatusBubble(self.aggregator)
496
 
        self.addCleanup(self.bubble.cleanup)
497
 
 
498
 
    def test_notification_not_shown_initially(self):
499
 
        """The notification is not shown initially."""
500
 
        self.assertEqual(None, self.bubble.notification)
501
 
 
502
 
    def test_show_pops_bubble(self):
503
 
        """The show method pops the bubble immediately."""
504
 
        self.bubble.show()
505
 
        self.assertEqual(1, len(self.bubble.notification.notifications_shown))
506
 
 
507
 
 
508
 
class FakeLauncher(object):
509
 
    """A fake Launcher."""
510
 
 
511
 
    progress_visible = False
512
 
    progress = 0.0
513
 
 
514
 
    def show_progressbar(self):
515
 
        """The progressbar is shown."""
516
 
        self.progress_visible = True
517
 
 
518
 
    def hide_progressbar(self):
519
 
        """The progressbar is hidden."""
520
 
        self.progress_visible = False
521
 
 
522
 
    def set_progress(self, value):
523
 
        """The progressbar value is changed."""
524
 
        self.progress = value
525
 
 
526
 
 
527
 
class FakeInhibitor(object):
528
 
    """A fake session inhibitor."""
529
 
 
530
 
    def inhibit(self, flags, reason):
531
 
        """Inhibit some events with a given reason."""
532
 
        self.flags = flags
533
 
        return defer.succeed(self)
534
 
 
535
 
    def cancel(self):
536
 
        """Cancel the inhibition for the current cookie."""
537
 
        self.flags = 0
538
 
        return defer.succeed(self)
539
 
 
540
 
 
541
 
class ProgressBarTestCase(TestCase):
542
 
    """Tests for the progress bar."""
543
 
 
544
 
    @defer.inlineCallbacks
545
 
    def setUp(self):
546
 
        """Initialize this test instance."""
547
 
        yield super(ProgressBarTestCase, self).setUp()
548
 
        self.patch(aggregator, "Launcher", FakeLauncher)
549
 
        self.clock = PatchedClock()
550
 
        self.bar = aggregator.ProgressBar(clock=self.clock)
551
 
        self.addCleanup(self.bar.cleanup)
552
 
        self.timeout_calls = []
553
 
        original_timeout = self.bar._timeout
554
 
 
555
 
        def fake_timeout(result):
556
 
            """A fake _timeout method."""
557
 
            self.timeout_calls.append(self.bar.progress)
558
 
            original_timeout(result)
559
 
 
560
 
        self.patch(self.bar, "_timeout", fake_timeout)
561
 
 
562
 
    def test_launcher_typeerror_nonfatal(self):
563
 
        """Test that Launcher raising TypeError is not fatal."""
564
 
        def raise_typeerror(*args, **kwargs):
565
 
            raise TypeError
566
 
 
567
 
        self.patch(aggregator, "Launcher", raise_typeerror)
568
 
        aggregator.ProgressBar(clock=self.clock)
569
 
 
570
 
    def test_shown_when_progress_made(self):
571
 
        """The progress bar is shown when progress is made."""
572
 
        self.bar.set_progress(0.5)
573
 
        self.assertTrue(self.bar.visible)
574
 
        self.assertTrue(self.bar.launcher.progress_visible)
575
 
 
576
 
    def test_progress_made_updates_counter(self):
577
 
        """Progress made updates the counter."""
578
 
        self.bar.set_progress(0.5)
579
 
        self.assertEqual(self.bar.progress, 0.5)
580
 
 
581
 
    def test_no_timer_set_initially(self):
582
 
        """There's no timer set initially."""
583
 
        self.assertEqual(self.bar.timer, None)
584
 
 
585
 
    def test_progress_made_sets_timer(self):
586
 
        """Progress made sets up a timer."""
587
 
        self.bar.set_progress(0.5)
588
 
        self.assertNotEqual(self.bar.timer, None)
589
 
 
590
 
    def test_cleanup_resets_timer(self):
591
 
        """The cleanup method resets the timer."""
592
 
        self.bar.set_progress(0.5)
593
 
        self.bar.cleanup()
594
 
        self.assertEqual(self.bar.timer, None)
595
 
 
596
 
    def test_progress_made_not_updated_initially(self):
597
 
        """Progress made is not updated initially."""
598
 
        self.bar.set_progress(0.5)
599
 
        self.assertEqual(0, len(self.timeout_calls))
600
 
        self.assertEqual(0.0, self.bar.launcher.progress)
601
 
 
602
 
    def test_progress_made_updated_after_a_delay(self):
603
 
        """The progressbar is updated after a delay."""
604
 
        self.bar.set_progress(0.5)
605
 
        self.clock.advance(aggregator.ProgressBar.updates_delay)
606
 
        self.assertIn(0.5, self.timeout_calls)
607
 
        self.assertEqual(0.5, self.bar.launcher.progress)
608
 
 
609
 
    def test_progress_updates_are_aggregated(self):
610
 
        """The progressbar is updated after a delay."""
611
 
        self.bar.set_progress(0.5)
612
 
        self.clock.advance(aggregator.ProgressBar.updates_delay / 2)
613
 
        self.bar.set_progress(0.6)
614
 
        self.clock.advance(aggregator.ProgressBar.updates_delay / 2)
615
 
        self.assertEqual(1, len(self.timeout_calls))
616
 
 
617
 
    def test_progress_updates_are_continuous(self):
618
 
        """The progressbar updates are continuous."""
619
 
        self.bar.set_progress(0.5)
620
 
        self.clock.advance(aggregator.ProgressBar.updates_delay)
621
 
        self.assertEqual(0.5, self.bar.launcher.progress)
622
 
        self.bar.set_progress(0.6)
623
 
        self.clock.advance(aggregator.ProgressBar.updates_delay)
624
 
        self.assertEqual(0.6, self.bar.launcher.progress)
625
 
        self.assertEqual(2, len(self.timeout_calls))
626
 
 
627
 
    def test_hidden_when_completed(self):
628
 
        """The progressbar is hidden when everything completes."""
629
 
        self.bar.set_progress(0.5)
630
 
        self.bar.completed()
631
 
        self.assertFalse(self.bar.visible)
632
 
        self.assertFalse(self.bar.launcher.progress_visible)
633
 
 
634
 
    @skipTest('Inhibitor is disabled to prevent bug #737620')
635
 
    @defer.inlineCallbacks
636
 
    def test_progress_made_inhibits_logout_suspend(self):
637
 
        """Suspend and logout are inhibited when the progressbar is shown."""
638
 
        self.bar.set_progress(0.5)
639
 
        expected = aggregator.session.INHIBIT_LOGOUT_SUSPEND
640
 
        inhibitor = yield self.bar.inhibitor_defer
641
 
        self.assertEqual(inhibitor.flags, expected)
642
 
 
643
 
    @skipTest('Inhibitor is disabled to prevent bug #737620')
644
 
    @defer.inlineCallbacks
645
 
    def test_completed_uninhibits_logout_suspend(self):
646
 
        """Suspend and logout are uninhibited when all has completed."""
647
 
        self.bar.set_progress(0.5)
648
 
        d = self.bar.inhibitor_defer
649
 
        self.bar.completed()
650
 
        inhibitor = yield d
651
 
        self.assertEqual(inhibitor.flags, 0)
652
 
 
653
 
 
654
 
class FakeDelayedBuffer(object):
655
 
    """Appends all status pushed into a list."""
656
 
    timer_reset = False
657
 
    processed = False
658
 
 
659
 
    def __init__(self, *args, **kwargs):
660
 
        """Initialize this instance."""
661
 
        self.events = []
662
 
 
663
 
    def push_event(self, event):
664
 
        """Push an event into this buffer."""
665
 
        self.events.append(event)
666
 
 
667
 
    def reset_threshold_timer(self):
668
 
        """The status has changed."""
669
 
        self.timer_reset = True
670
 
 
671
 
    def process_accumulated(self):
672
 
        """Process accumulated events."""
673
 
        self.processed = True
674
 
 
675
 
 
676
 
class FakeCommand(object):
677
 
    """A fake command."""
678
 
 
679
 
    def __init__(self, path=''):
680
 
        self.path = path
681
 
        self.share_id = path
682
 
        self.node_id = path
683
 
        self.deflated_size = 10000
684
 
        self.size = 0
685
 
 
686
 
 
687
 
class FakeUploadCommand(FakeCommand):
688
 
    """A fake upload."""
689
 
    def __init__(self, path=''):
690
 
        super(FakeUploadCommand, self).__init__(path)
691
 
        self.n_bytes_written = 0
692
 
 
693
 
 
694
 
class FakeDownloadCommand(FakeCommand):
695
 
    """A fake upload."""
696
 
    def __init__(self, path=''):
697
 
        super(FakeDownloadCommand, self).__init__(path)
698
 
        self.n_bytes_read = 0
699
 
 
700
 
 
701
 
class FakeVolumeManager(object):
702
 
    """A fake vm."""
703
 
 
704
 
    def __init__(self):
705
 
        """Initialize this instance."""
706
 
        self.volumes = {}
707
 
        self.root = None
708
 
 
709
 
    def get_volume(self, volume_id):
710
 
        """Return a volume given its id."""
711
 
        return self.volumes[volume_id]
712
 
 
713
 
 
714
 
class FakeAggregator(object):
715
 
    """A fake aggregator object."""
716
 
 
717
 
    def __init__(self, clock):
718
 
        """Initialize this fake instance."""
719
 
        self.queued_commands = set()
720
 
        self.notification_switch = aggregator.NotificationSwitch()
721
 
        self.connected = False
722
 
        self.clock = PatchedClock()
723
 
        self.files_uploading = []
724
 
        self.files_downloading = []
725
 
        self.progress_events = []
726
 
        self.recent_transfers = aggregator.deque(maxlen=10)
727
 
 
728
 
    def queue_done(self):
729
 
        """The queue completed all operations."""
730
 
        self.queued_commands.clear()
731
 
 
732
 
    def get_notification(self):
733
 
        """Create a new toggleable notification object."""
734
 
        return self.notification_switch.get_notification()
735
 
 
736
 
    def download_started(self, command):
737
 
        """A download just started."""
738
 
        self.files_downloading.append(command)
739
 
        self.queued_commands.add(command)
740
 
 
741
 
    def download_finished(self, command):
742
 
        """A download just finished."""
743
 
        if command in self.files_downloading:
744
 
            self.files_downloading.remove(command)
745
 
        self.recent_transfers.append(command.path)
746
 
        self.queued_commands.discard(command)
747
 
 
748
 
    def upload_started(self, command):
749
 
        """An upload just started."""
750
 
        self.files_uploading.append(command)
751
 
        self.queued_commands.add(command)
752
 
 
753
 
    def upload_finished(self, command):
754
 
        """An upload just finished."""
755
 
        if command in self.files_uploading:
756
 
            self.files_uploading.remove(command)
757
 
        self.recent_transfers.append(command.path)
758
 
        self.queued_commands.discard(command)
759
 
 
760
 
    def progress_made(self, share_id, node_id, n_bytes, deflated_size):
761
 
        """Progress made on up- or download."""
762
 
        self.progress_events.append(
763
 
            (share_id, node_id, n_bytes, deflated_size))
764
 
 
765
 
    def connection_made(self):
766
 
        """The client made the connection to the server."""
767
 
        self.connected = True
768
 
 
769
 
    def connection_lost(self):
770
 
        """The client lost the connection to the server."""
771
 
        self.connected = False
772
 
 
773
 
 
774
 
class StatusFrontendTestCase(BaseTwistedTestCase):
775
 
    """Test the status frontend."""
776
 
 
777
 
    @defer.inlineCallbacks
778
 
    def setUp(self):
779
 
        """Initialize this test instance."""
780
 
        yield super(StatusFrontendTestCase, self).setUp()
781
 
        self.patch(aggregator, "StatusAggregator", FakeAggregator)
782
 
        self.patch(aggregator, "ToggleableNotification",
783
 
                   FakeNotificationSingleton())
784
 
        self.fakefsm = None
785
 
        self.fakevm = FakeVolumeManager()
786
 
        self.status_frontend = aggregator.StatusFrontend()
787
 
        self.listener = status_listener.StatusListener(self.fakefsm,
788
 
                                                       self.fakevm,
789
 
                                                       self.status_frontend)
790
 
 
791
 
    def test_recent_transfers(self):
792
 
        """Check that it generates a tuple with the recent transfers."""
793
 
        self.patch(status_listener.action_queue, "Upload", FakeUploadCommand)
794
 
        self.patch(status_listener.action_queue, "Download",
795
 
                   FakeDownloadCommand)
796
 
 
797
 
        fake_command = FakeUploadCommand('path1')
798
 
        self.listener.handle_SYS_QUEUE_ADDED(fake_command)
799
 
        self.listener.handle_SYS_QUEUE_REMOVED(fake_command)
800
 
        fake_command = FakeUploadCommand('path2')
801
 
        self.listener.handle_SYS_QUEUE_ADDED(fake_command)
802
 
        self.listener.handle_SYS_QUEUE_REMOVED(fake_command)
803
 
        fake_command = FakeDownloadCommand('path3')
804
 
        self.listener.handle_SYS_QUEUE_ADDED(fake_command)
805
 
        self.listener.handle_SYS_QUEUE_REMOVED(fake_command)
806
 
        transfers = self.status_frontend.recent_transfers()
807
 
        expected = ['path1', 'path2', 'path3']
808
 
        self.assertEqual(transfers, expected)
809
 
 
810
 
        menu_data = self.listener.menu_data()
811
 
        self.assertEqual(
812
 
            menu_data,
813
 
            {UPLOADING: [],
814
 
             DOWNLOADING: [],
815
 
             RECENT_TRANSFERS: expected})
816
 
 
817
 
    def test_files_uploading(self):
818
 
        """Check that it returns a list with the path, size, and progress."""
819
 
        fc = FakeUploadCommand(path='testfile.txt')
820
 
        fc.deflated_size = 200
821
 
        self.status_frontend.upload_started(fc)
822
 
        uploading = self.status_frontend.files_uploading()
823
 
        expected = [('testfile.txt', 200, 0)]
824
 
        self.assertEqual(uploading, expected)
825
 
        menu_data = self.listener.menu_data()
826
 
        self.assertEqual(
827
 
            menu_data,
828
 
            {UPLOADING: expected,
829
 
             DOWNLOADING: [],
830
 
             RECENT_TRANSFERS: []})
831
 
 
832
 
        fc.deflated_size = 1000
833
 
        fc.n_bytes_written = 200
834
 
        fc2 = FakeUploadCommand(path='testfile2.txt')
835
 
        fc2.deflated_size = 2000
836
 
        fc2.n_bytes_written = 450
837
 
        self.status_frontend.upload_started(fc2)
838
 
        uploading = self.status_frontend.files_uploading()
839
 
        expected = [('testfile.txt', 1000, 200), ('testfile2.txt', 2000, 450)]
840
 
        self.assertEqual(uploading, expected)
841
 
 
842
 
        menu_data = self.listener.menu_data()
843
 
        self.assertEqual(
844
 
            menu_data,
845
 
            {UPLOADING: expected,
846
 
             DOWNLOADING: [],
847
 
             RECENT_TRANSFERS: []})
848
 
 
849
 
    def test_files_downloading(self):
850
 
        """Check that it returns a list with the path, size, and progress."""
851
 
        fc = FakeDownloadCommand(path='testfile.txt')
852
 
        fc.deflated_size = 200
853
 
        self.status_frontend.download_started(fc)
854
 
        downloading = self.status_frontend.files_downloading()
855
 
        expected = [('testfile.txt', 200, 0)]
856
 
        self.assertEqual(downloading, expected)
857
 
        menu_data = self.listener.menu_data()
858
 
        self.assertEqual(
859
 
            menu_data,
860
 
            {DOWNLOADING: expected,
861
 
             UPLOADING: [],
862
 
             RECENT_TRANSFERS: []})
863
 
 
864
 
        fc.deflated_size = 1000
865
 
        fc.n_bytes_read = 200
866
 
        fc2 = FakeDownloadCommand(path='testfile2.txt')
867
 
        fc2.deflated_size = 2000
868
 
        fc2.n_bytes_read = 450
869
 
        self.status_frontend.download_started(fc2)
870
 
        downloading = self.status_frontend.files_downloading()
871
 
        expected = [('testfile.txt', 1000, 200), ('testfile2.txt', 2000, 450)]
872
 
        self.assertEqual(downloading, expected)
873
 
 
874
 
        menu_data = self.listener.menu_data()
875
 
        self.assertEqual(
876
 
            menu_data,
877
 
            {DOWNLOADING: expected,
878
 
             UPLOADING: [],
879
 
             RECENT_TRANSFERS: []})
880
 
 
881
 
    def test_files_uploading_empty(self):
882
 
        """Check that empty files are ignored."""
883
 
        fc = FakeUploadCommand(path='testfile.txt')
884
 
        fc.deflated_size = None
885
 
        self.status_frontend.upload_started(fc)
886
 
 
887
 
        fc2 = FakeUploadCommand(path='testfile2.txt')
888
 
        fc2.deflated_size = 0
889
 
        fc2.n_bytes_written = 450
890
 
        self.status_frontend.upload_started(fc2)
891
 
        uploading = self.status_frontend.files_uploading()
892
 
        self.assertEqual(uploading, [])
893
 
 
894
 
    def test_files_downloading_empty(self):
895
 
        """Check that empty files are ignored."""
896
 
        fc = FakeDownloadCommand(path='testfile.txt')
897
 
        fc.deflated_size = None
898
 
        self.status_frontend.download_started(fc)
899
 
 
900
 
        fc2 = FakeDownloadCommand(path='testfile2.txt')
901
 
        fc2.deflated_size = 0
902
 
        fc2.n_bytes_written = 450
903
 
        self.status_frontend.download_started(fc2)
904
 
        downloading = self.status_frontend.files_downloading()
905
 
        self.assertEqual(downloading, [])
906
 
 
907
 
    def test_menu_data_full_response(self):
908
 
        """listener.menu_data returns uploading, downloading, and recent."""
909
 
        self.patch(status_listener.action_queue, "Upload", FakeUploadCommand)
910
 
        fake_command = FakeUploadCommand('path1')
911
 
        self.listener.handle_SYS_QUEUE_ADDED(fake_command)
912
 
        self.listener.handle_SYS_QUEUE_REMOVED(fake_command)
913
 
 
914
 
        self.patch(status_listener.action_queue, "Download",
915
 
                   FakeDownloadCommand)
916
 
        fake_command = FakeDownloadCommand('path2')
917
 
        self.listener.handle_SYS_QUEUE_ADDED(fake_command)
918
 
        self.listener.handle_SYS_QUEUE_REMOVED(fake_command)
919
 
 
920
 
        fc = FakeUploadCommand(path='testfile.txt')
921
 
        fc.deflated_size = 1000
922
 
        fc.n_bytes_written = 200
923
 
        self.status_frontend.upload_started(fc)
924
 
 
925
 
        fc = FakeDownloadCommand(path='download.pdf')
926
 
        fc.deflated_size = 10
927
 
        fc.n_bytes_read = 1
928
 
        self.status_frontend.download_started(fc)
929
 
 
930
 
        uploading = self.status_frontend.files_uploading()
931
 
        downloading = self.status_frontend.files_downloading()
932
 
        transfers = self.status_frontend.recent_transfers()
933
 
        expected = {UPLOADING: [('testfile.txt', 1000, 200)],
934
 
                    DOWNLOADING: [('download.pdf', 10, 1)],
935
 
                    RECENT_TRANSFERS: ['path1', 'path2']}
936
 
 
937
 
        self.assertEqual({UPLOADING: uploading,
938
 
                          DOWNLOADING: downloading,
939
 
                          RECENT_TRANSFERS: transfers},
940
 
                         expected)
941
 
 
942
 
    def test_file_published(self):
943
 
        """A file published event is processed."""
944
 
        share_id = "fake share id"
945
 
        node_id = "fake node id"
946
 
        is_public = True
947
 
        public_url = "http://fake_public/url"
948
 
        self.listener.handle_AQ_CHANGE_PUBLIC_ACCESS_OK(share_id, node_id,
949
 
                                                        is_public, public_url)
950
 
        self.assertEqual(
951
 
            2, len(self.status_frontend.notification.notifications_shown))
952
 
 
953
 
    def test_file_unpublished(self):
954
 
        """A file unpublished event is processed."""
955
 
        share_id = "fake share id"
956
 
        node_id = "fake node id"
957
 
        is_public = False
958
 
        public_url = None  # SD sends None when unpublishing
959
 
 
960
 
        self.listener.handle_AQ_CHANGE_PUBLIC_ACCESS_OK(share_id, node_id,
961
 
                                                        is_public, public_url)
962
 
        self.assertEqual(
963
 
            2, len(self.status_frontend.notification.notifications_shown))
964
 
 
965
 
    def test_download_started(self):
966
 
        """A download was added to the queue."""
967
 
        self.patch(status_listener.action_queue, "Download", FakeCommand)
968
 
        fake_command = FakeCommand()
969
 
        self.listener.handle_SYS_QUEUE_ADDED(fake_command)
970
 
        qc = self.status_frontend.aggregator.queued_commands
971
 
        self.assertIn(fake_command, qc)
972
 
 
973
 
    def test_download_started_with_no_deflated_size(self):
974
 
        """A download of unknown size was added to the queue."""
975
 
        self.patch(status_listener.action_queue, "Download", FakeCommand)
976
 
        fake_command = FakeCommand()
977
 
        fake_command.deflated_size = None
978
 
        self.listener.handle_SYS_QUEUE_ADDED(fake_command)
979
 
        qc = self.status_frontend.aggregator.queued_commands
980
 
        self.assertIn(fake_command, qc)
981
 
 
982
 
    def test_download_finished(self):
983
 
        """A download was removed from the queue."""
984
 
        self.patch(status_listener.action_queue, "Download", FakeCommand)
985
 
        fake_command = FakeCommand()
986
 
        self.listener.handle_SYS_QUEUE_ADDED(fake_command)
987
 
        self.listener.handle_SYS_QUEUE_REMOVED(fake_command)
988
 
        qc = self.status_frontend.aggregator.queued_commands
989
 
        self.assertNotIn(fake_command, qc)
990
 
 
991
 
    def test_upload_started(self):
992
 
        """An upload was added to the queue."""
993
 
        self.patch(status_listener.action_queue, "Upload", FakeCommand)
994
 
        fake_command = FakeCommand()
995
 
        self.listener.handle_SYS_QUEUE_ADDED(fake_command)
996
 
        qc = self.status_frontend.aggregator.queued_commands
997
 
        self.assertIn(fake_command, qc)
998
 
 
999
 
    def test_upload_started_with_no_deflated_size(self):
1000
 
        """An upload of unknown size was added to the queue."""
1001
 
        self.patch(status_listener.action_queue, "Upload", FakeCommand)
1002
 
        fake_command = FakeCommand()
1003
 
        fake_command.deflated_size = None
1004
 
        self.listener.handle_SYS_QUEUE_ADDED(fake_command)
1005
 
        qc = self.status_frontend.aggregator.queued_commands
1006
 
        self.assertIn(fake_command, qc)
1007
 
 
1008
 
    def test_upload_finished(self):
1009
 
        """An upload was removed from the queue."""
1010
 
        self.patch(status_listener.action_queue, "Upload", FakeCommand)
1011
 
        fake_command = FakeCommand()
1012
 
        self.listener.handle_SYS_QUEUE_ADDED(fake_command)
1013
 
        self.listener.handle_SYS_QUEUE_REMOVED(fake_command)
1014
 
        qc = self.status_frontend.aggregator.queued_commands
1015
 
        self.assertNotIn(fake_command, qc)
1016
 
 
1017
 
    def test_progress_made_on_upload(self):
1018
 
        """Progress was made on an uploading file."""
1019
 
        share_id = 'fake_share'
1020
 
        node_id = 'fake_node'
1021
 
        n_bytes_written = 100
1022
 
        deflated_size = 10000
1023
 
        self.listener.handle_AQ_UPLOAD_FILE_PROGRESS(
1024
 
            share_id=share_id, node_id=node_id,
1025
 
            n_bytes_written=n_bytes_written, deflated_size=deflated_size)
1026
 
        pe = self.status_frontend.aggregator.progress_events
1027
 
        self.assertEqual(
1028
 
            [(share_id, node_id, n_bytes_written, deflated_size)], pe,
1029
 
            "progress_made was not called (exactly once) on aggregator.")
1030
 
 
1031
 
    def test_progress_made_on_download(self):
1032
 
        """Progress was made on an downloading file."""
1033
 
        share_id = 'fake_share'
1034
 
        node_id = 'fake_node'
1035
 
        n_bytes_read = 200
1036
 
        deflated_size = 20000
1037
 
        self.listener.handle_AQ_DOWNLOAD_FILE_PROGRESS(
1038
 
            share_id=share_id, node_id=node_id,
1039
 
            n_bytes_read=n_bytes_read, deflated_size=deflated_size)
1040
 
        pe = self.status_frontend.aggregator.progress_events
1041
 
        self.assertEqual(
1042
 
            [(share_id, node_id, n_bytes_read, deflated_size)], pe,
1043
 
            "progress_made was not called (exactly once) on aggregator.")
1044
 
 
1045
 
    def test_queue_done(self):
1046
 
        """The queue is empty."""
1047
 
        fake_command = FakeCommand()
1048
 
        qc = self.status_frontend.aggregator.queued_commands
1049
 
        qc.add(fake_command)
1050
 
        self.listener.handle_SYS_QUEUE_DONE()
1051
 
        self.assertEqual(0, len(qc))
1052
 
 
1053
 
    def test_new_share_available(self):
1054
 
        """A new share is available for subscription."""
1055
 
        SHARE_ID = "fake share id"
1056
 
        FAKE_SENDER = 'Mom'
1057
 
        share = Share(volume_id=SHARE_ID, other_visible_name=FAKE_SENDER)
1058
 
        self.fakevm.volumes[SHARE_ID] = share
1059
 
        self.listener.handle_VM_SHARE_CREATED(SHARE_ID)
1060
 
        self.assertEqual(
1061
 
            2, len(self.status_frontend.notification.notifications_shown))
1062
 
 
1063
 
    def test_already_subscribed_new_udf_available(self):
1064
 
        """A new udf that was already subscribed."""
1065
 
        udf = UDF()
1066
 
        udf.subscribed = True
1067
 
        self.listener.handle_VM_UDF_CREATED(udf)
1068
 
        self.assertEqual(
1069
 
            1, len(self.status_frontend.notification.notifications_shown))
1070
 
 
1071
 
    def test_new_udf_available(self):
1072
 
        """A new udf is available for subscription."""
1073
 
        udf = UDF()
1074
 
        self.listener.handle_VM_UDF_CREATED(udf)
1075
 
        self.assertEqual(
1076
 
            2, len(self.status_frontend.notification.notifications_shown))
1077
 
 
1078
 
    def test_two_new_udfs_available(self):
1079
 
        """A new udf is available for subscription."""
1080
 
        udf1 = UDF()
1081
 
        self.listener.handle_VM_UDF_CREATED(udf1)
1082
 
        udf2 = UDF()
1083
 
        self.listener.handle_VM_UDF_CREATED(udf2)
1084
 
        self.assertEqual(
1085
 
            3, len(self.status_frontend.notification.notifications_shown))
1086
 
 
1087
 
    def test_server_connection_lost(self):
1088
 
        """The client connected to the server."""
1089
 
        self.status_frontend.aggregator.connected = True
1090
 
        self.listener.handle_SYS_CONNECTION_LOST()
1091
 
        self.assertEqual(
1092
 
            1, len(self.status_frontend.notification.notifications_shown))
1093
 
        self.assertFalse(self.status_frontend.aggregator.connected)
1094
 
 
1095
 
    def test_server_connection_made(self):
1096
 
        """The client connected to the server."""
1097
 
        self.status_frontend.aggregator.connected = False
1098
 
        self.listener.handle_SYS_CONNECTION_MADE()
1099
 
        self.assertEqual(
1100
 
            1, len(self.status_frontend.notification.notifications_shown))
1101
 
        self.assertTrue(self.status_frontend.aggregator.connected)
1102
 
 
1103
 
    def test_set_show_all_notifications(self):
1104
 
        """Test the set_show_all_notifications method."""
1105
 
        self.status_frontend.set_show_all_notifications(False)
1106
 
        self.assertFalse(self.status_frontend.aggregator.
1107
 
                         notification_switch.enabled)
1108
 
 
1109
 
    def test_udf_quota_exceeded(self):
1110
 
        """Quota exceeded in udf."""
1111
 
        mocker = Mocker()
1112
 
        launcher = mocker.replace(
1113
 
            "ubuntuone.platform.launcher.Launcher")
1114
 
        launcher()
1115
 
        mock_launcher = mocker.mock()
1116
 
        mocker.result(mock_launcher)
1117
 
        mock_launcher.set_urgent()
1118
 
        mocker.replay()
1119
 
        UDF_ID = 'fake udf id'
1120
 
        udf = UDF(volume_id=UDF_ID)
1121
 
        self.fakevm.volumes[UDF_ID] = udf
1122
 
        self.listener.handle_SYS_QUOTA_EXCEEDED(
1123
 
            volume_id=UDF_ID, free_bytes=0)
1124
 
        self.assertEqual(
1125
 
            1, len(self.status_frontend.notification.notifications_shown))
1126
 
        mocker.restore()
1127
 
        mocker.verify()
1128
 
 
1129
 
    def test_root_quota_exceeded(self):
1130
 
        """Quota exceeded in root."""
1131
 
        mocker = Mocker()
1132
 
        launcher = mocker.replace(
1133
 
            "ubuntuone.platform.launcher.Launcher")
1134
 
        launcher()
1135
 
        mock_launcher = mocker.mock()
1136
 
        mocker.result(mock_launcher)
1137
 
        mock_launcher.set_urgent()
1138
 
        mocker.replay()
1139
 
        ROOT_ID = 'fake root id'
1140
 
        root = Root(volume_id=ROOT_ID)
1141
 
        self.fakevm.volumes[ROOT_ID] = root
1142
 
        self.fakevm.root = root
1143
 
        self.listener.handle_SYS_QUOTA_EXCEEDED(
1144
 
            volume_id=ROOT_ID, free_bytes=0)
1145
 
        self.assertEqual(
1146
 
            1, len(self.status_frontend.notification.notifications_shown))
1147
 
        mocker.restore()
1148
 
        mocker.verify()
1149
 
 
1150
 
    def test_share_quota_exceeded(self):
1151
 
        """Quota exceeded in share."""
1152
 
        mocker = Mocker()
1153
 
        launcher = mocker.replace(
1154
 
            "ubuntuone.platform.launcher.Launcher")
1155
 
        launcher()
1156
 
        mock_launcher = mocker.mock()
1157
 
        mocker.result(mock_launcher)
1158
 
        mock_launcher.set_urgent()
1159
 
        launcher()
1160
 
        mock_launcher = mocker.mock()
1161
 
        mocker.result(mock_launcher)
1162
 
        mock_launcher.set_urgent()
1163
 
        mocker.replay()
1164
 
        SHARE_ID = 'fake share id'
1165
 
        BYTES = 0
1166
 
        share = Share(volume_id=SHARE_ID)
1167
 
        self.fakevm.volumes[SHARE_ID] = share
1168
 
        self.listener.handle_SYS_QUOTA_EXCEEDED(SHARE_ID, BYTES)
1169
 
        self.assertEqual(
1170
 
            2, len(self.status_frontend.notification.notifications_shown))
1171
 
        self.listener.handle_SYS_QUOTA_EXCEEDED(SHARE_ID, BYTES)
1172
 
        self.listener.handle_SYS_QUOTA_EXCEEDED(SHARE_ID, BYTES)
1173
 
        self.assertEqual(
1174
 
            2, len(self.status_frontend.notification.notifications_shown))
1175
 
        self.status_frontend.aggregator.clock.advance(aggregator.ONE_DAY + 1)
1176
 
        self.listener.handle_SYS_QUOTA_EXCEEDED(SHARE_ID, BYTES)
1177
 
        self.assertEqual(
1178
 
            3, len(self.status_frontend.notification.notifications_shown))
1179
 
        mocker.restore()
1180
 
        mocker.verify()
1181
 
 
1182
 
 
1183
 
class StatusEventTestCase(TestCase):
1184
 
    """Test the status event class and children."""
1185
 
 
1186
 
    CLASS = aggregator.StatusEvent
1187
 
    CLASS_KWARGS = {}
1188
 
    status = None
1189
 
 
1190
 
    @defer.inlineCallbacks
1191
 
    def setUp(self):
1192
 
        """Initialize this test instance."""
1193
 
        yield super(StatusEventTestCase, self).setUp()
1194
 
        if type(self) == StatusEventTestCase:
1195
 
            self.assertRaises(AssertionError, self.CLASS, **self.CLASS_KWARGS)
1196
 
        else:
1197
 
            self.status = self.CLASS(**self.CLASS_KWARGS)
1198
 
 
1199
 
    def test_one_message_defined(self):
1200
 
        """The singular message is defined as MESSAGE_ONE."""
1201
 
        if self.status:
1202
 
            self.assertNotEqual(None, self.CLASS.MESSAGE_ONE)
1203
 
 
1204
 
    def test_one_message_built_correctly(self):
1205
 
        """The message returned by one() is returned ok."""
1206
 
        if self.status:
1207
 
            self.assertEqual(self.status.one(), self.CLASS.MESSAGE_ONE)
1208
 
 
1209
 
 
1210
 
class FilePublishingStatusTestCase(StatusEventTestCase):
1211
 
    """Test the file publishing status class."""
1212
 
 
1213
 
    CLASS = aggregator.FilePublishingStatus
1214
 
    CLASS_KWARGS = {"new_public_url": "http://fake_public/url"}
1215
 
 
1216
 
    def test_one_message_built_correctly(self):
1217
 
        """The message returned by one() should include the url."""
1218
 
        expected = self.CLASS.MESSAGE_ONE % self.status.kwargs
1219
 
        self.assertEqual(self.status.one(), expected)
1220
 
 
1221
 
 
1222
 
class FileUnpublishingStatusTestCase(StatusEventTestCase):
1223
 
    """Test the file unpublishing status class."""
1224
 
 
1225
 
    CLASS = aggregator.FileUnpublishingStatus
1226
 
    CLASS_KWARGS = {"old_public_url": None}
1227
 
 
1228
 
 
1229
 
class ShareAvailableEventTestCase(StatusEventTestCase):
1230
 
    """Test the folder available status class with a Share."""
1231
 
 
1232
 
    FOLDER_NAME = "folder name"
1233
 
    OTHER_USER_NAME = "person name"
1234
 
    SAMPLE_SHARE = Share(accepted=False, name=FOLDER_NAME,
1235
 
                         other_visible_name=OTHER_USER_NAME)
1236
 
    CLASS = aggregator.ShareAvailableStatus
1237
 
    CLASS_KWARGS = {"share": SAMPLE_SHARE}
1238
 
 
1239
 
    def test_one_message_built_correctly(self):
1240
 
        """one() must return the folder name and user name."""
1241
 
        format_args = {
1242
 
            "folder_name": self.FOLDER_NAME,
1243
 
            "other_user_name": self.OTHER_USER_NAME,
1244
 
        }
1245
 
        expected = self.CLASS.MESSAGE_ONE % format_args
1246
 
        self.assertEqual(self.status.one(), expected)
1247
 
 
1248
 
 
1249
 
class UDFAvailableEventTestCase(StatusEventTestCase):
1250
 
    """Test the folder available status class with a UDF."""
1251
 
 
1252
 
    FOLDER_NAME = "folder name"
1253
 
    SAMPLE_UDF = UDF(subscribed=False, suggested_path=FOLDER_NAME)
1254
 
    CLASS = aggregator.UDFAvailableStatus
1255
 
    CLASS_KWARGS = {'udf': SAMPLE_UDF}
1256
 
 
1257
 
    def test_one_message_built_correctly(self):
1258
 
        """one() must return the folder name."""
1259
 
        format_args = {"folder_name": self.FOLDER_NAME}
1260
 
        expected = self.CLASS.MESSAGE_ONE % format_args
1261
 
        self.assertEqual(self.status.one(), expected)
1262
 
 
1263
 
 
1264
 
class ConnectionLostEventTestCase(StatusEventTestCase):
1265
 
    """Test the event when the connection is lost."""
1266
 
 
1267
 
    CLASS = aggregator.ConnectionLostStatus
1268
 
 
1269
 
    def test_many_message_built_correctly(self):
1270
 
        """The message returned by many() is returned ok."""
1271
 
        if self.status:
1272
 
            count = 99
1273
 
            test_events = [FakeStatus(88)] * count + [self.CLASS()]
1274
 
            expected = self.CLASS.MESSAGE_ONE
1275
 
            self.assertEqual(self.status.many(test_events), expected)
1276
 
 
1277
 
 
1278
 
class ConnectionMadeEventTestCase(ConnectionLostEventTestCase):
1279
 
    """Test the event when the connection is made."""
1280
 
 
1281
 
    CLASS = aggregator.ConnectionMadeStatus
1282
 
 
1283
 
 
1284
 
class FakeStatus(aggregator.StatusEvent):
1285
 
    """A fake status to test weight comparisons."""
1286
 
 
1287
 
    def __init__(self, weight):
1288
 
        """Initialize with the fake weight."""
1289
 
        super(FakeStatus, self).__init__()
1290
 
        self.WEIGHT = weight
1291
 
 
1292
 
 
1293
 
class FakeFileDiscoveryBubble(object):
1294
 
    """A fake FileDiscoveryBubble."""
1295
 
 
1296
 
    count = 0
1297
 
 
1298
 
    def __init__(self, status_aggregator, clock=None):
1299
 
        """Initialize this instance."""
1300
 
        self.status_aggregator = status_aggregator
1301
 
 
1302
 
    def new_file_found(self):
1303
 
        """New files were found."""
1304
 
        self.count += 1
1305
 
 
1306
 
    def cleanup(self):
1307
 
        """Cleanup this instance."""
1308
 
 
1309
 
    def connection_made(self):
1310
 
        """Connection made."""
1311
 
 
1312
 
    def connection_lost(self):
1313
 
        """Connection lost."""
1314
 
 
1315
 
 
1316
 
class FakeFinalBubble(object):
1317
 
    """A fake FinalStatusBubble."""
1318
 
 
1319
 
    shown = False
1320
 
 
1321
 
    def __init__(self, status_aggregator):
1322
 
        """Initialize this fake instance."""
1323
 
        self.status_aggregator = status_aggregator
1324
 
 
1325
 
    def cleanup(self):
1326
 
        """Cleanup this instance."""
1327
 
 
1328
 
    def show(self):
1329
 
        """Show this bubble."""
1330
 
        self.shown = True
1331
 
 
1332
 
 
1333
 
class StatusAggregatorTestCase(TestCase):
1334
 
    """Test the backend of the status aggregator."""
1335
 
 
1336
 
    @defer.inlineCallbacks
1337
 
    def setUp(self):
1338
 
        """Initialize this test instance."""
1339
 
        yield super(StatusAggregatorTestCase, self).setUp()
1340
 
        self.patch(aggregator, "FileDiscoveryBubble",
1341
 
                   FakeFileDiscoveryBubble)
1342
 
        self.patch(aggregator, "FinalStatusBubble",
1343
 
                   FakeFinalBubble)
1344
 
        self.patch(aggregator, "ToggleableNotification",
1345
 
                   FakeNotificationSingleton())
1346
 
        self.patch(aggregator, "Launcher", FakeLauncher)
1347
 
        clock = PatchedClock()
1348
 
        self.status_frontend = aggregator.StatusFrontend(clock=clock)
1349
 
        self.aggregator = self.status_frontend.aggregator
1350
 
        self.fake_bubble = self.aggregator.file_discovery_bubble
1351
 
 
1352
 
        self.handler = MementoHandler()
1353
 
        self.handler.setLevel(logging.DEBUG)
1354
 
        aggregator.logger.addHandler(self.handler)
1355
 
        aggregator.logger.setLevel(logging.DEBUG)
1356
 
        self.addCleanup(aggregator.logger.removeHandler, self.handler)
1357
 
        self.addCleanup(self.aggregator.progress_bar.cleanup)
1358
 
 
1359
 
    def assertStatusReset(self):
1360
 
        """Assert that the status is at zero."""
1361
 
        self.assertEqual(0, self.aggregator.download_done)
1362
 
        self.assertEqual(0, self.aggregator.upload_done)
1363
 
        self.assertEqual(0, len(self.aggregator.files_uploading))
1364
 
        self.assertEqual(0, len(self.aggregator.files_downloading))
1365
 
        self.assertEqual({}, self.aggregator.progress)
1366
 
        self.assertEqual({}, self.aggregator.to_do)
1367
 
        self.assertIdentical(None, self.aggregator.queue_done_timer)
1368
 
 
1369
 
    def test_register_progress_listener(self):
1370
 
        """Check that register listener handles properly additions."""
1371
 
 
1372
 
        def fake_callback():
1373
 
            """Do nothing."""
1374
 
 
1375
 
        self.aggregator.register_progress_listener(fake_callback)
1376
 
        self.assertEqual(len(self.aggregator.progress_listeners), 1)
1377
 
 
1378
 
    def test_register_progress_listener_fail(self):
1379
 
        """Check that register listener fails with not Callable objects."""
1380
 
        self.assertRaises(
1381
 
            TypeError, self.aggregator.register_progress_listener, [])
1382
 
        self.assertEqual(len(self.aggregator.progress_listeners), 0)
1383
 
 
1384
 
    def test_register_connection_listener(self):
1385
 
        """Check that register listener handles properly additions."""
1386
 
 
1387
 
        def fake_callback():
1388
 
            """Do nothing."""
1389
 
 
1390
 
        self.aggregator.register_connection_listener(fake_callback)
1391
 
        self.assertEqual(len(self.aggregator.connection_listeners), 1)
1392
 
 
1393
 
    def test_register_connection_listener_fail(self):
1394
 
        """Check that register listener fails with not Callable objects."""
1395
 
        self.assertRaises(
1396
 
            TypeError, self.aggregator.register_connection_listener, [])
1397
 
        self.assertEqual(len(self.aggregator.connection_listeners), 0)
1398
 
 
1399
 
    def test_connection_notifications(self):
1400
 
        """Check that the connection lister is notified."""
1401
 
        data = {}
1402
 
 
1403
 
        def fake_callback(status):
1404
 
            """Register status."""
1405
 
            data['status'] = status
1406
 
 
1407
 
        self.aggregator.register_connection_listener(fake_callback)
1408
 
        self.assertEqual(data, {})
1409
 
        self.aggregator.connection_lost()
1410
 
        self.assertFalse(data['status'])
1411
 
        self.aggregator.connection_made()
1412
 
        self.assertTrue(data['status'])
1413
 
 
1414
 
    def assertMiscCommandQueued(self, fc):
1415
 
        """Assert that some command was queued."""
1416
 
        self.assertEqual(len(self.aggregator.to_do), 1)
1417
 
        message = "queueing command (total: 1): %s" % fc.__class__.__name__
1418
 
        self.assertEqual(fc.deflated_size, sum(self.aggregator.to_do.values()))
1419
 
        self.assertTrue(self.handler.check_debug(message))
1420
 
        self.assertTrue(self.aggregator.progress_bar.visible)
1421
 
 
1422
 
    def assertMiscCommandUnqueued(self, fc):
1423
 
        """Assert that some command was unqueued."""
1424
 
        self.assertEqual(
1425
 
            1, self.aggregator.download_done + self.aggregator.upload_done)
1426
 
        message = "unqueueing command: %s" % fc.__class__.__name__
1427
 
        self.assertTrue(self.handler.check_debug(message))
1428
 
 
1429
 
    def test_counters_start_at_zero(self):
1430
 
        """Test that the counters start at zero."""
1431
 
        self.assertStatusReset()
1432
 
 
1433
 
    def test_file_download_started(self):
1434
 
        """Test that a file has started download."""
1435
 
        fc = FakeCommand(path='testfile.txt')
1436
 
        self.assertEqual('', self.aggregator.downloading_filename)
1437
 
        self.status_frontend.download_started(fc)
1438
 
        self.assertEqual(1, len(self.aggregator.files_downloading))
1439
 
        self.assertEqual('testfile.txt', self.aggregator.downloading_filename)
1440
 
        self.assertMiscCommandQueued(fc)
1441
 
        self.assertEqual(1, self.fake_bubble.count)
1442
 
        self.assertEqual(
1443
 
            {(fc.share_id, fc.node_id): (fc.deflated_size)},
1444
 
            self.aggregator.to_do)
1445
 
 
1446
 
    def test_file_download_finished(self):
1447
 
        """Test that a file has finished downloading."""
1448
 
        fc = FakeCommand()
1449
 
        self.status_frontend.download_started(fc)
1450
 
        self.status_frontend.download_finished(fc)
1451
 
        self.assertEqual(self.aggregator.download_done, 1)
1452
 
        self.assertMiscCommandUnqueued(fc)
1453
 
        self.assertEqual(
1454
 
            {(fc.share_id, fc.node_id): (fc.deflated_size)},
1455
 
            self.aggregator.progress)
1456
 
        self.assertEqual(len(self.aggregator.recent_transfers), 1)
1457
 
 
1458
 
    def test_file_upload_started(self):
1459
 
        """Test that a file has started upload."""
1460
 
        fc = FakeCommand(path='testfile.txt')
1461
 
        self.assertEqual('', self.aggregator.uploading_filename)
1462
 
        self.status_frontend.upload_started(fc)
1463
 
        self.assertEqual(1, len(self.aggregator.files_uploading))
1464
 
        self.assertEqual('testfile.txt', self.aggregator.uploading_filename)
1465
 
        self.assertMiscCommandQueued(fc)
1466
 
        self.assertEqual(1, self.fake_bubble.count)
1467
 
        self.assertEqual(
1468
 
            {(fc.share_id, fc.node_id): (fc.deflated_size)},
1469
 
            self.aggregator.to_do)
1470
 
 
1471
 
    def test_file_upload_finished(self):
1472
 
        """Test that a file has finished uploading."""
1473
 
        fc = FakeCommand()
1474
 
        self.status_frontend.upload_started(fc)
1475
 
        self.status_frontend.upload_finished(fc)
1476
 
        self.assertEqual(self.aggregator.upload_done, 1)
1477
 
        self.assertMiscCommandUnqueued(fc)
1478
 
        self.assertEqual(
1479
 
            {(fc.share_id, fc.node_id): (fc.deflated_size)},
1480
 
            self.aggregator.progress)
1481
 
        self.assertEqual(len(self.aggregator.recent_transfers), 1)
1482
 
 
1483
 
    def test_max_recent_files(self):
1484
 
        """Check that the queue doesn't exceed the 5 items."""
1485
 
        for i in range(10):
1486
 
            fc = FakeUploadCommand(str(i))
1487
 
            self.status_frontend.upload_started(fc)
1488
 
            self.status_frontend.upload_finished(fc)
1489
 
        for i in range(10):
1490
 
            fc = FakeDownloadCommand(str(i))
1491
 
            self.status_frontend.download_started(fc)
1492
 
            self.status_frontend.download_finished(fc)
1493
 
        self.assertEqual(len(self.aggregator.recent_transfers), 5)
1494
 
 
1495
 
    def test_recent_transfers_is_unique(self):
1496
 
        """Check that a given path is not repeated in recent transfers."""
1497
 
        fc = FakeDownloadCommand('hi')
1498
 
        self.status_frontend.download_started(fc)
1499
 
        self.status_frontend.download_finished(fc)
1500
 
        fc = FakeDownloadCommand('hi')
1501
 
        self.status_frontend.download_started(fc)
1502
 
        self.status_frontend.download_finished(fc)
1503
 
        self.assertEqual(len(self.aggregator.recent_transfers), 1)
1504
 
 
1505
 
    def test_recent_transfers_reorders(self):
1506
 
        """Check that if a transfer is repeated we put it back at the end."""
1507
 
        fc = FakeDownloadCommand('hi')
1508
 
        self.status_frontend.download_started(fc)
1509
 
        self.status_frontend.download_finished(fc)
1510
 
        fc = FakeDownloadCommand('howdy')
1511
 
        self.status_frontend.download_started(fc)
1512
 
        self.status_frontend.download_finished(fc)
1513
 
        fc = FakeUploadCommand('hi')
1514
 
        self.status_frontend.upload_started(fc)
1515
 
        self.status_frontend.upload_finished(fc)
1516
 
 
1517
 
        self.assertEqual(len(self.aggregator.recent_transfers), 2)
1518
 
        self.assertEqual(['howdy', 'hi'],
1519
 
                         list(self.aggregator.recent_transfers))
1520
 
 
1521
 
    def test_progress_made(self):
1522
 
        """Progress on up and downloads is tracked."""
1523
 
        share_id = 'fake_share'
1524
 
        node_id = 'fake_node'
1525
 
        n_bytes = 200
1526
 
        deflated_size = 100000
1527
 
        self.aggregator.progress_made(
1528
 
            share_id, node_id, n_bytes, deflated_size)
1529
 
        self.assertEqual(
1530
 
            {(share_id, node_id): (n_bytes)},
1531
 
            self.aggregator.progress)
1532
 
 
1533
 
    def test_get_discovery_message(self):
1534
 
        """Test the message that's shown on the discovery bubble."""
1535
 
        uploading = 10
1536
 
        downloading = 8
1537
 
        filename = 'upfile0.ext'
1538
 
        filename2 = 'downfile0.ext'
1539
 
        self.aggregator.files_uploading.extend([
1540
 
            FakeCommand(path='upfile%d.ext' % n) for n in range(uploading)])
1541
 
        self.aggregator.uploading_filename = filename
1542
 
        self.aggregator.files_downloading.extend([
1543
 
            FakeCommand(path='downfile%d.ext' % n) for n in
1544
 
            range(downloading)])
1545
 
        self.aggregator.downloading_filename = filename2
1546
 
        expected = (
1547
 
            aggregator.files_being_uploaded(filename, uploading) + "\n" +
1548
 
            aggregator.files_being_downloaded(filename2, downloading))
1549
 
        result = self.aggregator.get_discovery_message()
1550
 
        self.assertEqual(expected, result)
1551
 
 
1552
 
    def test_get_discovery_message_clears_filenames(self):
1553
 
        """Test the message that's shown on the discovery bubble."""
1554
 
        uploading = 10
1555
 
        downloading = 8
1556
 
        filename = 'upfile0.ext'
1557
 
        filename2 = 'downfile0.ext'
1558
 
        self.aggregator.files_uploading.extend([
1559
 
            FakeCommand(path='upfile%d.ext' % n) for n in range(uploading)])
1560
 
        self.aggregator.uploading_filename = filename
1561
 
        self.aggregator.files_downloading.extend([
1562
 
            FakeCommand(path='downfile%d.ext' % n) for n in
1563
 
            range(downloading)])
1564
 
        self.aggregator.downloading_filename = 'STALE FILENAME'
1565
 
        self.aggregator.uploading_filename = 'STALE FILENAME'
1566
 
        expected = (
1567
 
            aggregator.files_being_uploaded(filename, uploading) + "\n" +
1568
 
            aggregator.files_being_downloaded(filename2, downloading))
1569
 
        result = self.aggregator.get_discovery_message()
1570
 
        self.assertEqual(expected, result)
1571
 
 
1572
 
    def test_get_final_status_message(self):
1573
 
        """The final status message."""
1574
 
        done = (5, 10)
1575
 
        self.aggregator.uploading_filename = FILENAME
1576
 
        self.aggregator.downloading_filename = FILENAME2
1577
 
        self.aggregator.upload_done, self.aggregator.download_done = done
1578
 
 
1579
 
        expected = (
1580
 
            aggregator.FINAL_COMPLETED + "\n" +
1581
 
            aggregator.files_were_uploaded(
1582
 
                FILENAME, self.aggregator.upload_done) + "\n" +
1583
 
            aggregator.files_were_downloaded(
1584
 
                FILENAME2, self.aggregator.download_done))
1585
 
 
1586
 
        result = self.aggregator.get_final_status_message()
1587
 
        self.assertEqual(expected, result)
1588
 
 
1589
 
    def test_get_final_status_message_no_uploads(self):
1590
 
        """The final status message when there were no uploads."""
1591
 
        done = (0, 12)
1592
 
        self.aggregator.upload_done, self.aggregator.download_done = done
1593
 
        self.aggregator.downloading_filename = FILENAME2
1594
 
 
1595
 
        expected = (
1596
 
            aggregator.FINAL_COMPLETED + "\n" +
1597
 
            aggregator.files_were_downloaded(
1598
 
                FILENAME2, self.aggregator.download_done))
1599
 
 
1600
 
        result = self.aggregator.get_final_status_message()
1601
 
        self.assertEqual(expected, result)
1602
 
 
1603
 
    def test_get_final_status_message_no_downloads(self):
1604
 
        """The final status message when there were no downloads."""
1605
 
        done = (8, 0)
1606
 
        self.aggregator.upload_done, self.aggregator.download_done = done
1607
 
        self.aggregator.uploading_filename = FILENAME
1608
 
 
1609
 
        expected = (
1610
 
            aggregator.FINAL_COMPLETED + "\n" +
1611
 
            aggregator.files_were_uploaded(
1612
 
                FILENAME, self.aggregator.upload_done))
1613
 
 
1614
 
        result = self.aggregator.get_final_status_message()
1615
 
        self.assertEqual(expected, result)
1616
 
 
1617
 
    def test_queue_done_shows_bubble_when_downloads_happened(self):
1618
 
        """On queue done, show final bubble if downloads happened."""
1619
 
        fc = FakeCommand()
1620
 
        self.status_frontend.download_started(fc)
1621
 
        self.status_frontend.download_finished(fc)
1622
 
        old_final_bubble = self.aggregator.final_status_bubble
1623
 
        self.aggregator.queue_done()
1624
 
        self.aggregator.clock.advance(self.aggregator.finished_delay + 1)
1625
 
        self.assertTrue(old_final_bubble.shown)
1626
 
 
1627
 
    def test_queue_done_shows_bubble_when_uploads_happened(self):
1628
 
        """On queue done, show final bubble if uploads happened."""
1629
 
        fc = FakeCommand()
1630
 
        self.status_frontend.upload_started(fc)
1631
 
        self.status_frontend.upload_finished(fc)
1632
 
        old_final_bubble = self.aggregator.final_status_bubble
1633
 
        self.aggregator.queue_done()
1634
 
        self.aggregator.clock.advance(self.aggregator.finished_delay + 1)
1635
 
        self.assertTrue(old_final_bubble.shown)
1636
 
 
1637
 
    def test_queue_done_shows_bubble_only_after_delay(self):
1638
 
        """On queue_done, show final bubble only after a delay."""
1639
 
        fc = FakeCommand()
1640
 
        self.status_frontend.upload_started(fc)
1641
 
        self.status_frontend.upload_finished(fc)
1642
 
        old_final_bubble = self.aggregator.final_status_bubble
1643
 
        self.aggregator.queue_done()
1644
 
        self.assertFalse(old_final_bubble.shown)
1645
 
        self.aggregator.clock.advance(self.aggregator.finished_delay - 1)
1646
 
        self.assertFalse(old_final_bubble.shown)
1647
 
        self.aggregator.queue_done()
1648
 
        self.assertFalse(old_final_bubble.shown)
1649
 
        self.aggregator.clock.advance(2)
1650
 
        self.assertFalse(old_final_bubble.shown)
1651
 
        self.aggregator.clock.advance(self.aggregator.finished_delay + 1)
1652
 
        self.assertTrue(old_final_bubble.shown)
1653
 
 
1654
 
    def test_queue_done_does_not_show_bubble_when_no_transfers_happened(self):
1655
 
        """On queue done, don't show final bubble if no transfers happened."""
1656
 
        fc = FakeCommand()
1657
 
        self.status_frontend.upload_started(fc)
1658
 
        old_final_bubble = self.aggregator.final_status_bubble
1659
 
        self.aggregator.queue_done()
1660
 
        self.assertFalse(old_final_bubble.shown)
1661
 
 
1662
 
    def test_queue_done_resets_status_and_hides_progressbar(self):
1663
 
        """On queue done, reset counters and hide progressbar."""
1664
 
        fc = FakeCommand()
1665
 
        self.status_frontend.upload_started(fc)
1666
 
        self.aggregator.queue_done()
1667
 
        self.aggregator.clock.advance(self.aggregator.finished_delay + 1)
1668
 
        self.assertStatusReset()
1669
 
        self.assertEqual(0.0, self.aggregator.progress_bar.progress)
1670
 
        self.assertFalse(self.aggregator.progress_bar.visible)
1671
 
 
1672
 
    def test_download_started_cancels_timer(self):
1673
 
        """Starting a download cancels the queue_done timer."""
1674
 
        fc = FakeCommand()
1675
 
        self.status_frontend.download_started(fc)
1676
 
        self.aggregator.clock.advance(self.aggregator.finished_delay)
1677
 
        self.status_frontend.download_finished(fc)
1678
 
        self.aggregator.queue_done()
1679
 
        self.aggregator.clock.advance(self.aggregator.finished_delay / 2)
1680
 
        fc2 = FakeCommand()
1681
 
        self.status_frontend.download_started(fc2)
1682
 
        self.assertIdentical(self.aggregator.queue_done_timer, None)
1683
 
        self.aggregator.clock.advance(self.aggregator.finished_delay)
1684
 
        self.status_frontend.download_finished(fc2)
1685
 
 
1686
 
    def test_upload_started_cancels_timer(self):
1687
 
        """Starting an upload cancels the queue_done timer."""
1688
 
        fc = FakeCommand()
1689
 
        self.status_frontend.upload_started(fc)
1690
 
        self.aggregator.clock.advance(self.aggregator.finished_delay)
1691
 
        self.status_frontend.upload_finished(fc)
1692
 
        self.aggregator.queue_done()
1693
 
        self.aggregator.clock.advance(self.aggregator.finished_delay / 2)
1694
 
        fc2 = FakeCommand()
1695
 
        self.status_frontend.upload_started(fc2)
1696
 
        self.assertIdentical(self.aggregator.queue_done_timer, None)
1697
 
        self.aggregator.clock.advance(self.aggregator.finished_delay)
1698
 
        self.status_frontend.upload_finished(fc2)
1699
 
 
1700
 
 
1701
 
class StatusGrouperTestCase(TestCase):
1702
 
    """Tests for the group_statuses function."""
1703
 
 
1704
 
    def test_group_status(self):
1705
 
        """The status grouper sorts and groups by weight."""
1706
 
        status99 = FakeStatus(99)
1707
 
        statuses = [
1708
 
            status99,
1709
 
            status99,
1710
 
            FakeStatus(12),
1711
 
            FakeStatus(1)]
1712
 
 
1713
 
        result = [list(k) for _, k in aggregator.group_statuses(statuses)]
1714
 
        expected = [
1715
 
            [statuses[3]],
1716
 
            [statuses[2]],
1717
 
            [status99, status99]]
1718
 
 
1719
 
        self.assertEqual(result, expected)
1720
 
 
1721
 
 
1722
 
class HundredFeetTestCase(TestCase):
1723
 
    """Try to make all parts work together."""
1724
 
 
1725
 
    def test_all_together_now(self):
1726
 
        """Make all parts work together."""
1727
 
        self.patch(aggregator, "ToggleableNotification",
1728
 
                   FakeNotificationSingleton())
1729
 
        self.patch(aggregator, "Launcher", FakeLauncher)
1730
 
        self.patch(aggregator.session, "Inhibitor", FakeInhibitor)
1731
 
        clock = PatchedClock()
1732
 
        upload = FakeCommand(path='upload.foo')
1733
 
        sf = aggregator.StatusFrontend(clock=clock)
1734
 
        sf.server_connection_made()
1735
 
        sf.set_show_all_notifications(True)
1736
 
 
1737
 
        # the progress bar is not visible yet
1738
 
        self.assertFalse(sf.aggregator.progress_bar.visible)
1739
 
        sf.upload_started(upload)
1740
 
        # the progress bar is now shown
1741
 
        self.assertTrue(sf.aggregator.progress_bar.visible)
1742
 
        notifications_shown = (sf.aggregator.file_discovery_bubble.
1743
 
                               notification.notifications_shown)
1744
 
        # no notifications shown yet
1745
 
        self.assertEqual(0, len(notifications_shown))
1746
 
        clock.advance(aggregator.FileDiscoveryGatheringState.initial_delay)
1747
 
        # files found notification
1748
 
        self.assertEqual(1, len(notifications_shown))
1749
 
        download = FakeCommand('download.bar')
1750
 
        sf.download_started(download)
1751
 
        self.assertEqual(1, len(notifications_shown))
1752
 
        # the progress still is zero
1753
 
        self.assertEqual(0.0, sf.aggregator.progress_bar.progress)
1754
 
        clock.advance(aggregator.FileDiscoveryUpdateState.updates_delay)
1755
 
        # files count update
1756
 
        self.assertEqual(2, len(notifications_shown))
1757
 
        clock.advance(aggregator.FileDiscoveryUpdateState.updates_timeout -
1758
 
                      aggregator.FileDiscoveryUpdateState.updates_delay)
1759
 
        sf.upload_finished(upload)
1760
 
        sf.download_finished(download)
1761
 
        # the progress still is now 100%
1762
 
        self.assertEqual(1.0, sf.aggregator.progress_bar.progress)
1763
 
        sf.queue_done()
1764
 
        clock.advance(sf.aggregator.finished_delay + 1)
1765
 
        self.assertEqual(3, len(notifications_shown))
1766
 
 
1767
 
    def test_all_together_now_off(self):
1768
 
        """Make all parts work together, but with notifications off."""
1769
 
        self.patch(aggregator, "ToggleableNotification",
1770
 
                   FakeNotificationSingleton())
1771
 
        self.patch(aggregator, "Launcher", FakeLauncher)
1772
 
        self.patch(aggregator.session, "Inhibitor", FakeInhibitor)
1773
 
        clock = PatchedClock()
1774
 
        upload = FakeCommand('upload.foo')
1775
 
        sf = aggregator.StatusFrontend(clock=clock)
1776
 
        sf.set_show_all_notifications(False)
1777
 
 
1778
 
        # the progress bar is not visible yet
1779
 
        self.assertFalse(sf.aggregator.progress_bar.visible)
1780
 
        sf.upload_started(upload)
1781
 
        # the progress bar is now shown
1782
 
        self.assertTrue(sf.aggregator.progress_bar.visible)
1783
 
        notifications_shown = (sf.aggregator.file_discovery_bubble.
1784
 
                               notification.notifications_shown)
1785
 
        # no notifications shown, never
1786
 
        self.assertEqual(0, len(notifications_shown))
1787
 
        clock.advance(aggregator.FileDiscoveryGatheringState.initial_delay)
1788
 
        self.assertEqual(0, len(notifications_shown))
1789
 
        download = FakeCommand('download.bar')
1790
 
        sf.download_started(download)
1791
 
        self.assertEqual(0, len(notifications_shown))
1792
 
        # the progress still is zero
1793
 
        self.assertEqual(0.0, sf.aggregator.progress_bar.progress)
1794
 
        clock.advance(aggregator.FileDiscoveryUpdateState.updates_delay)
1795
 
        self.assertEqual(0, len(notifications_shown))
1796
 
        clock.advance(aggregator.FileDiscoveryUpdateState.updates_timeout -
1797
 
                      aggregator.FileDiscoveryUpdateState.updates_delay)
1798
 
        sf.upload_finished(upload)
1799
 
        sf.download_finished(download)
1800
 
        # the progress still is now 100%
1801
 
        self.assertEqual(1.0, sf.aggregator.progress_bar.progress)
1802
 
        self.assertEqual(0, len(notifications_shown))
1803
 
        sf.queue_done()
1804
 
        self.assertEqual(0, len(notifications_shown))
1805
 
 
1806
 
HundredFeetTestCase.skip = "libindicate-ERROR causes core dump: #922179"