35
136
def send_notification(self, title, message, icon=None, append=False):
36
137
"""Show a notification to the user."""
37
138
self.notifications_shown.append((title, message, icon, append))
40
class DelayedBufferTestCase(TestCase):
41
"""Test the delayed buffer class."""
48
"""Initialize this test instance."""
51
append = lambda *args: self.result.append(args)
52
self.ag = aggregator.DelayedBuffer(cb=append, clock=self.clock,
53
threshold=self.THRESHOLD)
55
def test_a_bunch_at_once_aggregated_as_one(self):
56
"""A bunch of messages sent at once are aggregated into one."""
57
for n in range(self.COUNT_MESSAGES):
58
self.ag.push("message1")
59
self.clock.advance(self.THRESHOLD)
60
self.assertEqual(len(self.result), 1)
62
def test_a_bunch_under_threshold_aggregated_as_one(self):
63
"""A bunch of messages sent under threshold are aggregated into one."""
64
for n in range(self.COUNT_MESSAGES):
65
self.ag.push("message1")
66
self.clock.advance(self.SMALL_DELAY)
67
self.clock.advance(self.THRESHOLD)
68
self.assertEqual(len(self.result), 1)
70
def test_a_bunch_over_threshold_are_not_aggregated(self):
71
"""A bunch of messages sent over the threshold are not aggregated."""
72
for n in range(self.COUNT_MESSAGES):
73
self.ag.push("message1")
74
self.clock.advance(self.THRESHOLD)
75
self.clock.advance(self.THRESHOLD + self.SMALL_DELAY)
76
self.assertEqual(len(self.result), self.COUNT_MESSAGES)
79
class FakeAggregator(object):
139
return len(self.notifications_shown) - 1
141
def update_notification(self, notification, new_title, new_message,
143
"""Update the notification with a new message body."""
144
title, old_message, icon, append = self.notifications_shown[
146
if new_icon is not None:
148
# we store it as a new notification, to ease testing
149
notification_params = (new_title, new_message, icon, append)
150
self.notifications_shown.append(notification_params)
153
def FakeNotificationSingleton():
154
"""Builds a notification singleton, that logs all notifications shown."""
155
instance = FakeNotification()
158
"""Returns the single instance."""
164
class FakeMessaging(AbstractMessaging):
165
"""A fake messaging class."""
167
def __init__(self): # pylint: disable=W0231
168
self.messages_shown = {}
169
self.messages_updated = {}
171
# pylint: disable=R0913
172
def show_message(self, sender, callback=None, message_time=None,
173
message_count=None, icon=None):
174
"""Show a message to the user."""
175
if message_count and sender in self.messages_shown:
176
self.update_count(sender, message_count)
177
self.messages_shown[sender] = (
178
callback, message_time, message_count, icon)
179
# pylint: enable=R0913
181
def update_count(self, sender, add_count):
182
"""Update the count for an existing indicator."""
183
self.messages_updated[sender] = (sender, add_count)
186
class FakeStatusAggregator(object):
187
"""A fake status aggregator."""
189
def __init__(self, clock): # pylint: disable=W0613
190
"""Initialize this instance."""
193
self.restart_progress_bubble_called = False
195
def get_discovery_message(self):
196
"""Return the file discovery message."""
198
return self.build_discovery_message()
200
def build_discovery_message(self):
201
"""Build the file discovery message."""
202
return "a lot of files found (%d).""" % self.discovered
204
def get_progress_message(self):
205
"""Return the progress message."""
207
return self.build_progress_message()
209
def build_progress_message(self):
210
"""Build the progress message."""
211
params = (self.discovered, self.completed)
212
return "a lot of files transferring (%d/%d).""" % params
214
def get_final_status_message(self):
215
"""Return the final status message."""
216
return "a lot of files completed."""
218
def restart_progress_bubble(self):
219
"""Reset the progress bubble."""
220
self.restart_progress_bubble_called = True
223
class FileDiscoveryBubbleTestCase(TestCase):
224
"""Test the FileDiscoveryBubble class."""
227
"""Initialize this test instance."""
228
self.patch(aggregator, "Notification", FakeNotificationSingleton())
229
self.clock = PatchedClock()
230
self.aggregator = FakeStatusAggregator(clock=self.clock)
231
self.bubble = aggregator.FileDiscoveryBubble(self.aggregator,
233
self.addCleanup(self.bubble.cleanup)
234
fdis = aggregator.FileDiscoveryGatheringState
235
self.initial_delay = fdis.initial_delay
236
self.smaller_delay = self.initial_delay * 0.8
237
self.initial_timeout = fdis.initial_timeout
238
fdus = aggregator.FileDiscoveryUpdateState
239
self.updates_delay = fdus.updates_delay
240
self.updates_timeout = fdus.updates_timeout
241
fdss = aggregator.FileDiscoverySleepState
242
self.sleep_delay = fdss.sleep_delay
244
self.handler = MementoHandler()
245
self.handler.setLevel(logging.DEBUG)
246
aggregator.logger.addHandler(self.handler)
247
aggregator.logger.setLevel(logging.DEBUG)
248
self.addCleanup(aggregator.logger.removeHandler, self.handler)
251
def get_notifications_shown(self):
252
"""The list of notifications shown."""
253
return self.bubble.notification.notifications_shown
255
def test_popup_shows_notification(self):
256
"""The popup callback shows notifications."""
257
self.bubble.new_file_found()
259
message = self.aggregator.build_discovery_message()
260
notification = (aggregator.UBUNTUONE_TITLE, message, None, False)
261
self.assertIn(notification, self.get_notifications_shown())
263
def test_popup_resets_progress_bubble(self):
264
"""The popup callback resets the progress bubble."""
265
self.bubble.new_file_found()
267
self.assertTrue(self.aggregator.restart_progress_bubble_called)
269
def test_notification_is_logged_in_debug(self):
270
"""The notification is printed in the debug log."""
271
self.bubble.new_file_found()
273
msg = "notification shown: %s" % self.get_notifications_shown()[0][1]
274
self.assertTrue(self.handler.check_debug(msg))
276
def test_bubble_is_not_shown_initially(self):
277
"""The bubble is not shown initially."""
278
self.bubble.new_file_found()
279
self.assertEqual(0, len(self.get_notifications_shown()))
281
def test_bubble_is_shown_after_delay(self):
282
"""The bubble is shown after a delay."""
283
self.bubble.new_file_found()
284
self.clock.advance(self.initial_delay)
285
self.assertEqual(1, len(self.get_notifications_shown()))
287
def test_bubble_not_shown_if_more_files_found(self):
288
"""The bubble is not shown if more files found within delay."""
289
self.clock.advance(self.smaller_delay)
290
self.bubble.new_file_found()
291
self.clock.advance(self.smaller_delay)
292
self.assertEqual(0, len(self.get_notifications_shown()))
294
def test_bubble_shown_if_timeout_exceeded(self):
295
"""The bubble is shown if the timeout is exceeded."""
296
self.bubble.new_file_found()
297
count = int(self.initial_timeout / self.smaller_delay) + 1
298
for n in range(count):
299
self.clock.advance(self.smaller_delay)
300
self.bubble.new_file_found()
301
self.assertEqual(1, len(self.get_notifications_shown()))
303
def test_idle_state(self):
304
"""The idle state is verified."""
305
self.assertEqual(type(self.bubble.state),
306
aggregator.FileDiscoveryIdleState)
308
def test_gathering_state(self):
309
"""The gathering state is set after the first file is found."""
310
self.bubble.new_file_found()
311
self.assertEqual(type(self.bubble.state),
312
aggregator.FileDiscoveryGatheringState)
314
def test_update_state(self):
315
"""When the gathering state finishes, the update state is started."""
316
self.bubble.new_file_found()
317
self.clock.advance(self.initial_delay)
318
self.assertEqual(type(self.bubble.state),
319
aggregator.FileDiscoveryUpdateState)
321
def test_sleeping_state(self):
322
"""When the update state finishes, the sleeping state is started."""
323
self.bubble.new_file_found()
324
self.clock.advance(self.initial_delay)
325
self.clock.advance(self.updates_timeout)
326
self.assertEqual(type(self.bubble.state),
327
aggregator.FileDiscoverySleepState)
329
def test_back_to_initial_state(self):
330
"""When the last state finishes, we return to the idle state."""
331
self.bubble.new_file_found()
332
self.clock.advance(self.initial_delay)
333
self.clock.advance(self.updates_timeout)
334
self.clock.advance(self.sleep_delay)
335
self.assertEqual(type(self.bubble.state),
336
aggregator.FileDiscoveryIdleState)
338
def test_new_files_found_while_updating_not_shown_immediately(self):
339
"""New files found in the updating state are not shown immediately."""
340
self.bubble.new_file_found()
341
self.clock.advance(self.initial_delay)
342
self.bubble.new_file_found()
343
self.assertEqual(1, len(self.get_notifications_shown()))
345
def test_new_files_found_while_updating_are_shown_after_a_delay(self):
346
"""New files found in the updating state are shown after a delay."""
347
self.bubble.new_file_found()
348
self.clock.advance(self.initial_delay)
349
self.bubble.new_file_found()
350
self.clock.advance(self.updates_delay)
351
self.assertEqual(2, len(self.get_notifications_shown()))
353
def test_update_resets_progress_bubble(self):
354
"""The update callback resets the progress bubble."""
355
self.bubble.new_file_found()
357
self.bubble.new_file_found()
358
self.bubble._update()
359
self.assertTrue(self.aggregator.restart_progress_bubble_called)
361
def test_update_modifies_notification(self):
362
"""The update callback updates notifications."""
363
self.bubble.new_file_found()
365
self.bubble.new_file_found()
366
self.bubble._update()
367
message = self.aggregator.build_discovery_message()
368
notification = (aggregator.UBUNTUONE_TITLE, message, None, False)
369
self.assertIn(notification, self.get_notifications_shown())
371
def test_update_is_logged_in_debug(self):
372
"""The notification is logged when _update is called."""
373
self.bubble.new_file_found()
375
self.bubble.new_file_found()
376
self.bubble._update()
377
msg = "notification updated: %s" % self.get_notifications_shown()[1][1]
378
self.assertTrue(self.handler.check_debug(msg))
381
class ProgressBubbleTestCase(TestCase):
382
"""Tests for the progress bubble."""
385
"""Initialize this test instance."""
386
self.patch(aggregator, "Notification", FakeNotificationSingleton())
387
self.clock = PatchedClock()
388
self.aggregator = FakeStatusAggregator(clock=self.clock)
389
self.bubble = aggregator.ProgressBubble(self.aggregator,
391
self.addCleanup(self.bubble.cleanup)
392
self.smaller_delay = aggregator.ProgressBubble.sleep_delay * 0.8
393
self.right_delay = aggregator.ProgressBubble.sleep_delay
394
self.longer_delay = aggregator.ProgressBubble.sleep_delay * 1.2
396
def test_is_created_successfully(self):
397
"""The progress bubble is created and idle."""
398
self.assertEqual(None, self.bubble.timer)
400
def test_restart_creates_timer(self):
401
"""Restarting creates a timer."""
402
self.bubble.restart()
403
self.assertNotEqual(None, self.bubble.timer)
405
def test_not_shown_initially(self):
406
"""The bubble is not shown initially."""
407
self.bubble.restart()
408
self.assertEqual(None, self.bubble.notification)
410
def test_not_shown_immediately(self):
411
"""The bubble is not shown immediately."""
412
self.bubble.restart()
413
self.clock.advance(self.smaller_delay)
414
self.assertEqual(None, self.bubble.notification)
416
def test_shown_after_delay(self):
417
"""The bubble is shown after the standard delay."""
418
self.bubble.restart()
419
self.clock.advance(self.longer_delay)
420
self.assertEqual(1, len(self.bubble.notification.notifications_shown))
422
def test_not_shown_if_restarted(self):
423
"""The delay is adjusted when restarting."""
424
self.bubble.restart()
425
self.clock.advance(self.smaller_delay)
426
self.bubble.restart()
427
self.clock.advance(self.smaller_delay)
428
self.assertEqual(None, self.bubble.notification)
430
def test_delay_reached_after_restart(self):
431
"""The timeout is still reached after a restart."""
432
self.bubble.restart()
433
self.clock.advance(self.smaller_delay)
434
self.bubble.restart()
435
self.clock.advance(self.longer_delay)
436
self.assertEqual(1, len(self.bubble.notification.notifications_shown))
438
def test_can_be_stopped(self):
439
"""The timeout can be stopped."""
440
self.bubble.restart()
441
self.clock.advance(self.smaller_delay)
443
self.clock.advance(self.longer_delay)
444
self.assertEqual(None, self.bubble.notification)
446
def test_delay_reached_after_stopping_then_restarting(self):
447
"""The timeout is still reached after a stop followed by a restart."""
448
self.bubble.restart()
449
self.clock.advance(self.smaller_delay)
451
self.clock.advance(self.longer_delay)
452
self.bubble.restart()
453
self.clock.advance(self.longer_delay)
454
self.assertEqual(1, len(self.bubble.notification.notifications_shown))
456
def test_restarts_automatically(self):
457
"""The timer is restarted automatically."""
458
self.bubble.restart()
459
self.clock.advance(self.right_delay)
460
self.assertEqual(1, len(self.bubble.notification.notifications_shown))
461
self.clock.advance(self.right_delay)
462
self.assertEqual(2, len(self.bubble.notification.notifications_shown))
465
class FinalBubbleTestCase(TestCase):
466
"""Test for the final status notification bubble."""
469
"""Initialize this test instance."""
470
self.patch(aggregator, "Notification", FakeNotificationSingleton())
471
self.clock = PatchedClock()
472
self.aggregator = FakeStatusAggregator(clock=self.clock)
473
self.bubble = aggregator.FinalStatusBubble(self.aggregator)
474
self.addCleanup(self.bubble.cleanup)
476
def test_notification_not_shown_initially(self):
477
"""The notification is not shown initially."""
478
self.assertEqual(None, self.bubble.notification)
480
def test_show_pops_bubble(self):
481
"""The show method pops the bubble immediately."""
483
self.assertEqual(1, len(self.bubble.notification.notifications_shown))
486
class ProgressBarTestCase(TestCase):
487
"""Tests for the progress bar."""
490
"""Initialize this test instance."""
491
self.clock = PatchedClock()
492
self.bar = aggregator.ProgressBar(clock=self.clock)
493
self.addCleanup(self.bar.cleanup)
494
self.timeout_calls = []
495
original_timeout = self.bar._timeout
497
def fake_timeout(result):
498
"""A fake _timeout method."""
499
self.timeout_calls.append(self.bar.percentage)
500
original_timeout(result)
502
self.patch(self.bar, "_timeout", fake_timeout)
504
def test_shown_when_progress_made(self):
505
"""The progress bar is shown when progress is made."""
506
self.bar.progress_made(50, 100)
507
self.assertTrue(self.bar.visible)
509
def test_progress_made_updates_counter(self):
510
"""Progress made updates the counter."""
511
self.bar.progress_made(50, 100)
512
self.assertEqual(self.bar.percentage, 50.0)
514
def test_no_timer_set_initially(self):
515
"""There's no timer set initially."""
516
self.assertEqual(self.bar.timer, None)
518
def test_progress_made_sets_timer(self):
519
"""Progress made sets up a timer."""
520
self.bar.progress_made(50, 100)
521
self.assertNotEqual(self.bar.timer, None)
523
def test_progress_made_not_updated_initially(self):
524
"""Progress made is not updated initially."""
525
self.bar.progress_made(50, 100)
526
self.assertEqual(0, len(self.timeout_calls))
528
def test_progress_made_updated_after_a_delay(self):
529
"""The progressbar is updated after a delay."""
530
self.bar.progress_made(50, 100)
531
self.clock.advance(aggregator.ProgressBar.updates_delay)
532
self.assertIn(50.0, self.timeout_calls)
534
def test_progress_updates_are_aggregated(self):
535
"""The progressbar is updated after a delay."""
536
self.bar.progress_made(50, 100)
537
self.clock.advance(aggregator.ProgressBar.updates_delay / 2)
538
self.bar.progress_made(60, 100)
539
self.clock.advance(aggregator.ProgressBar.updates_delay / 2)
540
self.assertEqual(1, len(self.timeout_calls))
542
def test_progress_updates_are_continuous(self):
543
"""The progressbar updates are continuous."""
544
self.bar.progress_made(50, 100)
545
self.clock.advance(aggregator.ProgressBar.updates_delay)
546
self.bar.progress_made(60, 100)
547
self.clock.advance(aggregator.ProgressBar.updates_delay)
548
self.assertEqual(2, len(self.timeout_calls))
550
def test_hidden_when_completed(self):
551
"""The progressbar is hidden when everything completes."""
552
self.bar.progress_made(50, 100)
554
self.assertFalse(self.bar.visible)
556
def test_disable(self):
557
"""The bar pulse is disabled."""
560
self.assertFalse(self.bar.pulsating)
562
def test_enable(self):
563
"""The bar pulse is enabled."""
566
self.assertTrue(self.bar.pulsating)
569
class FakeDelayedBuffer(object):
80
570
"""Appends all status pushed into a list."""
82
574
def __init__(self, *args, **kwargs):
83
575
"""Initialize this instance."""
86
def push(self, event):
87
self.statuses.append(event)
90
class StatusAggregatorFrontendTestCase(TestCase):
91
"""Test the status events aggregator."""
578
def push_event(self, event):
579
"""Push an event into this buffer."""
580
self.events.append(event)
582
def reset_threshold_timer(self):
583
"""The status has changed."""
584
self.timer_reset = True
586
def process_accumulated(self):
587
"""Process accumulated events."""
588
self.processed = True
591
class FakeCommand(object):
592
"""A fake command."""
595
class FakeVolumeManager(object):
599
"""Initialize this instance."""
602
def get_volume(self, volume_id):
603
"""Return a volume given its id."""
604
return self.volumes[volume_id]
607
class FakeAggregator(object):
608
"""A fake aggregator object."""
610
def __init__(self, clock):
611
"""Initialize this fake instance."""
612
self.queued_commands = set()
614
def queue_done(self):
615
"""The queue completed all operations."""
616
self.queued_commands.clear()
618
def misc_command_queued(self, command):
619
"""A new command was queued."""
620
self.queued_commands.add(command)
622
def misc_command_unqueued(self, command):
623
"""A new command was unqueued."""
624
self.queued_commands.discard(command)
626
download_started = misc_command_queued
627
download_finished = misc_command_unqueued
628
upload_started = misc_command_queued
629
upload_finished = misc_command_unqueued
632
class StatusFrontendTestCase(TestCase):
633
"""Test the status frontend."""
94
636
"""Initialize this test instance."""
637
self.patch(aggregator, "StatusAggregator", FakeAggregator)
638
self.patch(aggregator, "Notification", FakeNotificationSingleton())
639
self.patch(aggregator, "Messaging", FakeMessaging)
95
640
self.fakefsm = None
97
self.patch(aggregator, "DelayedBuffer", FakeAggregator)
98
self.status_aggregator = aggregator.StatusAggregator()
99
self.listener = StatusListener(self.fakefsm, self.fakevm,
100
self.status_aggregator)
641
self.fakevm = FakeVolumeManager()
642
self.status_frontend = aggregator.StatusFrontend()
643
self.listener = status_listener.StatusListener(self.fakefsm,
645
self.status_frontend)
102
647
def test_file_published(self):
103
648
"""A file published event is processed."""
105
650
node_id = "fake node id"
107
652
public_url = "http://fake_public/url"
109
653
self.listener.handle_AQ_CHANGE_PUBLIC_ACCESS_OK(share_id, node_id,
110
654
is_public, public_url)
111
status = self.status_aggregator.aggregator.statuses[0]
112
self.assertIsInstance(status, aggregator.FilePublishingStatus)
656
1, len(self.status_frontend.notification.notifications_shown))
658
(aggregator.UBUNTUONE_TITLE,
659
'A file was just made public at http://fake_public/url', None,
661
self.status_frontend.notification.notifications_shown[0])
114
663
def test_file_unpublished(self):
115
664
"""A file unpublished event is processed."""
116
665
share_id = "fake share id"
117
666
node_id = "fake node id"
118
667
is_public = False
119
public_url = None # SD sends None when unpublishing
668
public_url = None # SD sends None when unpublishing
121
670
self.listener.handle_AQ_CHANGE_PUBLIC_ACCESS_OK(share_id, node_id,
122
671
is_public, public_url)
123
status = self.status_aggregator.aggregator.statuses[0]
124
self.assertIsInstance(status, aggregator.FileUnpublishingStatus)
127
class FakeOtherStatus(aggregator.StatusEvent):
128
"""A fake status to test weight comparisons."""
130
def __init__(self, weight):
131
"""Initialize with the fake weight."""
132
super(FakeOtherStatus, self).__init__()
673
1, len(self.status_frontend.notification.notifications_shown))
675
(aggregator.UBUNTUONE_TITLE, 'A file is no longer published', None,
677
self.status_frontend.notification.notifications_shown[0])
679
def test_download_started(self):
680
"""A download was added to the queue."""
681
self.patch(status_listener.action_queue, "Download", FakeCommand)
682
fake_command = FakeCommand()
683
self.listener.handle_SYS_QUEUE_ADDED(fake_command)
684
qc = self.status_frontend.aggregator.queued_commands
685
self.assertIn(fake_command, qc)
687
def test_download_finished(self):
688
"""A download was removed from the queue."""
689
self.patch(status_listener.action_queue, "Download", FakeCommand)
690
fake_command = FakeCommand()
691
self.listener.handle_SYS_QUEUE_ADDED(fake_command)
692
self.listener.handle_SYS_QUEUE_REMOVED(fake_command)
693
qc = self.status_frontend.aggregator.queued_commands
694
self.assertNotIn(fake_command, qc)
696
def test_upload_started(self):
697
"""A upload was added to the queue."""
698
self.patch(status_listener.action_queue, "Upload", FakeCommand)
699
fake_command = FakeCommand()
700
self.listener.handle_SYS_QUEUE_ADDED(fake_command)
701
qc = self.status_frontend.aggregator.queued_commands
702
self.assertIn(fake_command, qc)
704
def test_upload_finished(self):
705
"""A upload was removed from the queue."""
706
self.patch(status_listener.action_queue, "Upload", FakeCommand)
707
fake_command = FakeCommand()
708
self.listener.handle_SYS_QUEUE_ADDED(fake_command)
709
self.listener.handle_SYS_QUEUE_REMOVED(fake_command)
710
qc = self.status_frontend.aggregator.queued_commands
711
self.assertNotIn(fake_command, qc)
713
def test_queue_added(self):
714
"""A command was added to the queue."""
715
fake_command = FakeCommand()
716
self.listener.handle_SYS_QUEUE_ADDED(fake_command)
717
qc = self.status_frontend.aggregator.queued_commands
718
self.assertIn(fake_command, qc)
720
def test_queue_removed(self):
721
"""A command has finished and is removed from the queue."""
722
fake_command = FakeCommand()
723
self.listener.handle_SYS_QUEUE_ADDED(fake_command)
724
self.listener.handle_SYS_QUEUE_REMOVED(fake_command)
725
qc = self.status_frontend.aggregator.queued_commands
726
self.assertNotIn(fake_command, qc)
728
def test_queue_done(self):
729
"""The queue is empty."""
730
fake_command = FakeCommand()
731
qc = self.status_frontend.aggregator.queued_commands
733
self.listener.handle_SYS_QUEUE_DONE()
734
self.assertEqual(0, len(qc))
736
def test_new_share_available(self):
737
"""A new share is available for subscription."""
738
SHARE_ID = "fake share id"
740
share = Share(volume_id=SHARE_ID, other_visible_name=FAKE_SENDER)
741
self.fakevm.volumes[SHARE_ID] = share
742
self.listener.handle_VM_SHARE_CREATED(SHARE_ID)
744
1, len(self.status_frontend.notification.notifications_shown))
746
(aggregator.UBUNTUONE_TITLE,
747
'New cloud folder available: <%s> shared by <%s>' % (
748
'None', FAKE_SENDER), None, False),
749
self.status_frontend.notification.notifications_shown[0])
750
msg = self.status_frontend.messaging.messages_shown[FAKE_SENDER]
751
# msg did not receive a time argument
752
self.assertEqual(None, msg[1])
753
# msg did not receive a count argument
754
self.assertEqual(None, msg[2])
756
def test_new_udf_available(self):
757
"""A new udf is available for subscription."""
759
self.listener.handle_VM_UDF_CREATED(udf)
761
1, len(self.status_frontend.notification.notifications_shown))
763
(aggregator.UBUNTUONE_TITLE, 'New cloud folder available: None',
765
self.status_frontend.notification.notifications_shown[0])
767
1, len(self.status_frontend.messaging.messages_shown))
769
0, len(self.status_frontend.messaging.messages_updated))
770
msg = self.status_frontend.messaging.messages_shown[
771
aggregator.NEW_UDFS_SENDER]
772
# msg did not receive a time argument
773
self.assertEqual(None, msg[1])
774
# msg did receive a count argument
775
self.assertEqual(1, msg[2])
777
def test_two_new_udfs_available(self):
778
"""A new udf is available for subscription."""
780
self.listener.handle_VM_UDF_CREATED(udf1)
782
self.listener.handle_VM_UDF_CREATED(udf2)
784
2, len(self.status_frontend.notification.notifications_shown))
786
(aggregator.UBUNTUONE_TITLE, 'New cloud folder available: None',
788
self.status_frontend.notification.notifications_shown[0])
790
(aggregator.UBUNTUONE_TITLE, 'New cloud folder available: None',
792
self.status_frontend.notification.notifications_shown[1])
794
1, len(self.status_frontend.messaging.messages_shown))
796
1, len(self.status_frontend.messaging.messages_updated))
797
msg = self.status_frontend.messaging.messages_shown[
798
aggregator.NEW_UDFS_SENDER]
799
# msg did not receive a time argument
800
self.assertEqual(None, msg[1])
801
# msg did receive a count argument
802
self.assertEqual(1, msg[2])
804
(aggregator.NEW_UDFS_SENDER, 1),
805
self.status_frontend.messaging.messages_updated[
806
aggregator.NEW_UDFS_SENDER])
808
def test_server_connection_lost(self):
809
"""The client connected to the server."""
810
self.listener.handle_SYS_CONNECTION_LOST()
812
1, len(self.status_frontend.notification.notifications_shown))
814
(aggregator.UBUNTUONE_TITLE,
815
aggregator.ConnectionLostStatus.MESSAGE_ONE, None, False),
816
self.status_frontend.notification.notifications_shown[0])
818
def test_server_connection_made(self):
819
"""The client connected to the server."""
820
self.listener.handle_SYS_CONNECTION_MADE()
822
1, len(self.status_frontend.notification.notifications_shown))
824
(aggregator.UBUNTUONE_TITLE,
825
aggregator.ConnectionMadeStatus.MESSAGE_ONE, None, False),
826
self.status_frontend.notification.notifications_shown[0])
135
829
class StatusEventTestCase(TestCase):
137
830
"""Test the status event class and children."""
139
832
CLASS = aggregator.StatusEvent
185
883
CLASS_ARGS = (None,)
188
class StatusAggregatorBackendTestCase(TestCase):
189
"""Test the callback of the status aggregator."""
886
class ShareAvailableEventTestCase(StatusEventTestCase):
887
"""Test the folder available status class with a Share."""
889
FOLDER_NAME = "folder name"
890
OTHER_USER_NAME = "person name"
891
SAMPLE_SHARE = Share(accepted=False, name=FOLDER_NAME,
892
other_visible_name=OTHER_USER_NAME)
893
CLASS = aggregator.ShareAvailableStatus
894
CLASS_ARGS = (SAMPLE_SHARE,)
896
def test_one_message_built_correctly(self):
897
"""one() must return the folder name and user name."""
898
params = (self.FOLDER_NAME, self.OTHER_USER_NAME)
899
expected = self.CLASS.MESSAGE_ONE % params
900
self.assertEqual(self.status.one(), expected)
903
class UDFAvailableEventTestCase(StatusEventTestCase):
904
"""Test the folder available status class with a UDF."""
906
FOLDER_NAME = "folder name"
907
SAMPLE_UDF = UDF(subscribed=False, suggested_path=FOLDER_NAME)
908
CLASS = aggregator.UDFAvailableStatus
909
CLASS_ARGS = (SAMPLE_UDF,)
911
def test_one_message_built_correctly(self):
912
"""one() must return the folder name."""
913
params = (self.FOLDER_NAME,)
914
expected = self.CLASS.MESSAGE_ONE % params
915
self.assertEqual(self.status.one(), expected)
918
class ConnectionLostEventTestCase(StatusEventTestCase):
919
"""Test the event when the connection is lost."""
921
CLASS = aggregator.ConnectionLostStatus
924
def test_many_message_built_correctly(self):
925
"""The message returned by many() is returned ok."""
928
test_events = [FakeStatus(88)] * count + [self.CLASS()]
929
expected = self.CLASS.MESSAGE_ONE
930
self.assertEqual(self.status.many(test_events), expected)
933
class ConnectionMadeEventTestCase(ConnectionLostEventTestCase):
934
"""Test the event when the connection is made."""
936
CLASS = aggregator.ConnectionMadeStatus
940
class FakeStatus(aggregator.StatusEvent):
941
"""A fake status to test weight comparisons."""
943
def __init__(self, weight):
944
"""Initialize with the fake weight."""
945
super(FakeStatus, self).__init__()
949
class FakeFileDiscoveryBubble(object):
950
"""A fake FileDiscoveryBubble."""
954
def __init__(self, status_aggregator, clock=None):
955
"""Initialize this instance."""
956
self.status_aggregator = status_aggregator
958
def new_file_found(self):
959
"""New files were found."""
963
"""Cleanup this instance."""
966
class FakeProgressBubble(object):
967
"""A fake ProgressBubble object."""
972
def __init__(self, status_aggregator, clock=None):
973
"""Initialize this instance."""
974
self.status_aggregator = status_aggregator
977
"""Cleanup this instance."""
980
"""Start this bubble waiting."""
984
"""Make this bubble stop."""
987
class FakeFinalBubble(object):
988
"""A fake FinalStatusBubble."""
992
def __init__(self, status_aggregator):
993
"""Initialize this fake instance."""
994
self.status_aggregator = status_aggregator
997
"""Cleanup this instance."""
1000
"""Show this bubble."""
1004
class StatusAggregatorTestCase(TestCase):
1005
"""Test the backend of the status aggregator."""
191
1007
def setUp(self):
192
1008
"""Initialize this test instance."""
193
self.patch(aggregator, "Notification", FakeNotification)
194
self.patch(aggregator, "DelayedBuffer", FakeAggregator)
195
self.status_aggregator = aggregator.StatusAggregator()
196
self.fake_notification = self.status_aggregator.notification
198
def test_pushed_one(self):
199
"""One event pushed."""
200
public_url = "http://fake_public/url"
201
event = aggregator.FilePublishingStatus(public_url)
203
self.status_aggregator.callback(events)
204
message = aggregator.FilePublishingStatus.MESSAGE_ONE % public_url
205
notification = (aggregator.UBUNTUONE_TITLE, message, None, False)
206
self.assertIn(notification, self.fake_notification.notifications_shown)
208
def test_pushed_many_same_type(self):
209
"""Pushed many events, all of the same type."""
210
public_url = "http://fake_public/url%d"
214
for n in range(count):
215
event = aggregator.FilePublishingStatus(public_url % n)
218
self.status_aggregator.callback(events)
219
message = aggregator.FilePublishingStatus.MESSAGE_MANY % count
220
notification = (aggregator.UBUNTUONE_TITLE, message, None, False)
221
self.assertIn(notification, self.fake_notification.notifications_shown)
223
def test_pushed_many_mixed_types(self):
224
"""Pushed many events, of mixed types."""
225
public_url = "http://fake_public/url%d"
229
for n in range(count):
230
event = aggregator.FilePublishingStatus(public_url % n)
232
event = aggregator.FileUnpublishingStatus(public_url % n)
235
self.status_aggregator.callback(events)
236
lines = [aggregator.FilePublishingStatus.MESSAGE_MANY % count,
237
aggregator.FileUnpublishingStatus.MESSAGE_MANY % count]
238
message = "\n".join(lines)
239
notification = (aggregator.UBUNTUONE_TITLE, message, None, False)
240
self.assertIn(notification, self.fake_notification.notifications_shown)
1009
self.patch(aggregator, "FileDiscoveryBubble",
1010
FakeFileDiscoveryBubble)
1011
self.patch(aggregator, "ProgressBubble",
1013
self.patch(aggregator, "FinalStatusBubble",
1015
self.status_frontend = aggregator.StatusFrontend()
1016
self.aggregator = self.status_frontend.aggregator
1017
self.fake_bubble = self.aggregator.file_discovery_bubble
1019
self.handler = MementoHandler()
1020
self.handler.setLevel(logging.DEBUG)
1021
aggregator.logger.addHandler(self.handler)
1022
aggregator.logger.setLevel(logging.DEBUG)
1023
self.addCleanup(aggregator.logger.removeHandler, self.handler)
1024
self.addCleanup(self.aggregator.progress_bar.cleanup)
1026
def assertStatusReset(self):
1027
"""Assert that the status is at zero."""
1028
self.assertEqual(self.aggregator.download_total, 0)
1029
self.assertEqual(self.aggregator.download_done, 0)
1030
self.assertEqual(self.aggregator.upload_total, 0)
1031
self.assertEqual(self.aggregator.upload_done, 0)
1032
self.assertEqual(self.aggregator.total_counter, 0)
1033
self.assertEqual(self.aggregator.done_counter, 0)
1034
self.assertEqual(len(self.aggregator.files_uploading), 0)
1035
self.assertEqual(len(self.aggregator.files_downloading), 0)
1037
def assertMiscCommandQueued(self, fc):
1038
"""Assert that some command was queued."""
1039
self.assertEqual(self.aggregator.total_counter, 1)
1040
message = "queueing command (0/1): %s" % fc.__class__.__name__
1041
self.assertTrue(self.handler.check_debug(message))
1042
self.assertTrue(self.aggregator.progress_bar.visible)
1044
def assertMiscCommandUnqueued(self, fc):
1045
"""Assert that some command was unqueued."""
1046
self.assertEqual(self.aggregator.done_counter, 1)
1047
message = "unqueueing command (1/1): %s" % fc.__class__.__name__
1048
self.assertTrue(self.handler.check_debug(message))
1050
def test_counters_start_at_zero(self):
1051
"""Test that the counters start at zero."""
1052
self.assertStatusReset()
1054
def test_misc_command_queue(self):
1055
"""Test that a misc command was queued."""
1057
self.status_frontend.queue_added(fc)
1058
self.assertMiscCommandQueued(fc)
1059
self.assertEqual(0, self.aggregator.progress_bar.percentage)
1061
def test_misc_command_unqueue(self):
1062
"""Test that a misc command was unqueued."""
1064
self.status_frontend.queue_added(fc)
1065
self.status_frontend.queue_removed(fc)
1066
self.assertMiscCommandUnqueued(fc)
1067
self.assertEqual(100.0, self.aggregator.progress_bar.percentage)
1069
def test_file_download_started(self):
1070
"""Test that a file has started download."""
1072
self.status_frontend.download_started(fc)
1073
self.assertEqual(self.aggregator.download_total, 1)
1074
self.assertMiscCommandQueued(fc)
1075
self.assertEqual(1, self.fake_bubble.count)
1077
def test_file_download_finished(self):
1078
"""Test that a file has finished downloading."""
1080
self.status_frontend.download_started(fc)
1081
self.status_frontend.download_finished(fc)
1082
self.assertEqual(self.aggregator.download_done, 1)
1083
self.assertMiscCommandUnqueued(fc)
1085
def test_file_upload_started(self):
1086
"""Test that a file has started upload."""
1088
self.status_frontend.upload_started(fc)
1089
self.assertEqual(self.aggregator.upload_total, 1)
1090
self.assertMiscCommandQueued(fc)
1091
self.assertEqual(1, self.fake_bubble.count)
1093
def test_file_upload_finished(self):
1094
"""Test that a file has finished uploading."""
1096
self.status_frontend.upload_started(fc)
1097
self.status_frontend.upload_finished(fc)
1098
self.assertEqual(self.aggregator.upload_done, 1)
1099
self.assertMiscCommandUnqueued(fc)
1101
def test_get_discovery_message(self):
1102
"""Test the message that's shown on the discovery bubble."""
1105
self.aggregator.files_uploading.extend(range(uploading))
1106
self.aggregator.files_downloading.extend(range(downloading))
1107
expected = (aggregator.FILES_UPLOADING % uploading + "\n" +
1108
aggregator.FILES_DOWNLOADING % downloading)
1109
result = self.aggregator.get_discovery_message()
1110
self.assertEqual(expected, result)
1112
def test_get_progress_message(self):
1113
"""Test the message that's shown on the progress bubble."""
1114
self.aggregator.upload_done = 5
1115
self.aggregator.upload_total = 10
1116
self.aggregator.download_done = 3
1117
self.aggregator.download_total = 8
1118
self.aggregator.done_counter = 9
1119
self.aggregator.total_counter = 20
1120
expected = (aggregator.PROGRESS_UPLOADED % (5, 10) + " " +
1121
aggregator.PROGRESS_DOWNLOADED % (3, 8) + " " +
1122
aggregator.PROGRESS_COMPLETED % int(100.0 * 9 / 20))
1123
result = self.aggregator.get_progress_message()
1124
self.assertEqual(expected, result)
1126
def test_get_progress_message_no_uploads(self):
1127
"""The progress message when no uploads are going on."""
1128
self.aggregator.upload_done = 0
1129
self.aggregator.upload_total = 0
1130
self.aggregator.download_done = 3
1131
self.aggregator.download_total = 8
1132
self.aggregator.done_counter = 9
1133
self.aggregator.total_counter = 20
1134
expected = (aggregator.PROGRESS_DOWNLOADED % (3, 8) + " " +
1135
aggregator.PROGRESS_COMPLETED % int(100.0 * 9 / 20))
1136
result = self.aggregator.get_progress_message()
1137
self.assertEqual(expected, result)
1139
def test_get_progress_message_no_downloads(self):
1140
"""The progress message when no downloads are going on."""
1141
self.aggregator.upload_done = 5
1142
self.aggregator.upload_total = 10
1143
self.aggregator.download_done = 0
1144
self.aggregator.download_total = 0
1145
self.aggregator.done_counter = 9
1146
self.aggregator.total_counter = 20
1147
expected = (aggregator.PROGRESS_UPLOADED % (5, 10) + " " +
1148
aggregator.PROGRESS_COMPLETED % int(100.0 * 9 / 20))
1149
result = self.aggregator.get_progress_message()
1150
self.assertEqual(expected, result)
1152
def test_get_progress_message_no_total(self):
1153
"""No progress message possible if total counter is zero."""
1154
self.aggregator.total_counter = 0
1155
self.assertRaises(AssertionError, self.aggregator.get_progress_message)
1157
def test_get_final_status_message(self):
1158
"""The final status message."""
1160
self.aggregator.upload_done, self.aggregator.download_done = done
1161
expected = (aggregator.FINAL_COMPLETED + "\n" +
1162
aggregator.FINAL_UPLOADED % done[0] + "\n" +
1163
aggregator.FINAL_DOWNLOADED % done[1])
1164
result = self.aggregator.get_final_status_message()
1165
self.assertEqual(expected, result)
1167
def test_get_final_status_message_no_uploads(self):
1168
"""The final status message when there were no uploads."""
1170
self.aggregator.upload_done, self.aggregator.download_done = done
1171
expected = (aggregator.FINAL_COMPLETED + "\n" +
1172
aggregator.FINAL_DOWNLOADED % done[1])
1173
result = self.aggregator.get_final_status_message()
1174
self.assertEqual(expected, result)
1176
def test_get_final_status_message_no_downloads(self):
1177
"""The final status message when there were no downloads."""
1179
self.aggregator.upload_done, self.aggregator.download_done = done
1180
expected = (aggregator.FINAL_COMPLETED + "\n" +
1181
aggregator.FINAL_UPLOADED % done[0])
1182
result = self.aggregator.get_final_status_message()
1183
self.assertEqual(expected, result)
1185
def test_started_progress_bubble(self):
1186
"""The progress bubble is started."""
1187
self.aggregator.restart_progress_bubble()
1188
self.assertTrue(self.aggregator.progress_bubble.started)
1190
def test_queue_done(self):
1191
"""On queue done, show final bubble and reset counters."""
1193
self.status_frontend.upload_started(fc)
1194
old_final_bubble = self.aggregator.final_status_bubble
1195
self.aggregator.queue_done()
1196
self.assertTrue(old_final_bubble.shown)
1197
self.assertStatusReset()
1198
self.assertEqual(0.0, self.aggregator.progress_bar.percentage)
1199
self.assertFalse(self.aggregator.progress_bar.visible)
1202
class StatusGrouperTestCase(TestCase):
1203
"""Tests for the group_statuses function."""
1205
def test_group_status(self):
1206
"""The status grouper sorts and groups by weight."""
1207
status99 = FakeStatus(99)
1215
result = [ list(k) for j, k in aggregator.group_statuses(statuses) ]
1219
[status99, status99],
1222
self.assertEqual(result, expected)
1225
class HundredFeetTestCase(TestCase):
1226
"""Try to make all parts work together."""
1228
def test_all_together_now(self):
1229
"""Make all parts work together."""
1230
self.patch(aggregator, "Notification", FakeNotificationSingleton())
1231
clock = PatchedClock()
1232
upload = FakeCommand()
1233
sf = aggregator.StatusFrontend(clock=clock)
1234
clock.advance(aggregator.ProgressBubble.sleep_delay)
1235
# the progress bar is not visible yet
1236
self.assertFalse(sf.aggregator.progress_bar.visible)
1237
sf.upload_started(upload)
1238
# the progress bar is now shown
1239
self.assertTrue(sf.aggregator.progress_bar.visible)
1240
notifications_shown = (sf.aggregator.file_discovery_bubble.
1241
notification.notifications_shown)
1242
# no notifications shown yet
1243
self.assertEqual(0, len(notifications_shown))
1244
clock.advance(aggregator.FileDiscoveryGatheringState.initial_delay)
1245
# files found notification
1246
self.assertEqual(1, len(notifications_shown))
1247
download = FakeCommand()
1248
sf.download_started(download)
1249
self.assertEqual(1, len(notifications_shown))
1250
# the progress still is zero
1251
self.assertEqual(0.0, sf.aggregator.progress_bar.percentage)
1252
clock.advance(aggregator.FileDiscoveryUpdateState.updates_delay)
1253
# files count update
1254
self.assertEqual(2, len(notifications_shown))
1255
clock.advance(aggregator.FileDiscoveryUpdateState.updates_timeout -
1256
aggregator.FileDiscoveryUpdateState.updates_delay)
1257
sf.upload_finished(upload)
1258
# the progress bubble has no notifications yet
1259
self.assertEqual(None, sf.aggregator.progress_bubble.notification)
1260
clock.advance(aggregator.ProgressBubble.sleep_delay)
1261
# the progress bubble is shown
1262
self.assertEqual(3, len(notifications_shown))
1263
sf.upload_finished(download)
1264
# the progress still is now 100%
1265
self.assertEqual(100.0, sf.aggregator.progress_bar.percentage)
1266
# progress, but the progress bubble is not updated immediately
1267
self.assertEqual(3, len(notifications_shown))
1268
clock.advance(aggregator.ProgressBubble.sleep_delay)
1269
# the progress bubble is updated after a delay
1270
self.assertEqual(4, len(notifications_shown))
1272
# the final bubble is shown immediately
1273
self.assertEqual(5, len(notifications_shown))
1274
clock.advance(aggregator.ProgressBubble.sleep_delay * 2)
1275
# no more notifications are shown
1276
self.assertEqual(5, len(notifications_shown))