131
134
def __init__(self, application_name="fake app"):
132
135
"""Initialize this instance."""
133
136
self.notifications_shown = []
137
self.notification_switch = None
134
138
self.application_name = application_name
136
140
def send_notification(self, title, message, icon=None, append=False):
137
141
"""Show a notification to the user."""
142
if (self.notification_switch is not None
143
and not self.notification_switch.enabled):
138
145
self.notifications_shown.append((title, message, icon, append))
139
146
return len(self.notifications_shown) - 1
141
def update_notification(self, notification, new_title, new_message,
148
def update_notification(self, notification_id, new_title, new_message,
143
150
"""Update the notification with a new message body."""
151
if (self.notification_switch is not None
152
and not self.notification_switch.enabled):
154
if notification_id is None:
144
156
title, old_message, icon, append = self.notifications_shown[
146
158
if new_icon is not None:
148
160
# we store it as a new notification, to ease testing
219
233
"""Reset the progress bubble."""
220
234
self.restart_progress_bubble_called = True
236
def build_notification(self):
237
"""Create a new toggleable notification object."""
238
return self.notification_switch.build_notification()
241
class ToggleableNotificationTestCase(TestCase):
242
"""Test the ToggleableNotification class."""
245
"""Initialize this test instance."""
246
self.patch(aggregator, "Notification", FakeNotification)
247
self.notification_switch = aggregator.NotificationSwitch()
248
self.toggleable = self.notification_switch.build_notification()
250
def assertShown(self, notification):
251
"""Assert that the notification was shown."""
252
self.assertIn(notification,
253
self.toggleable.notification.notifications_shown)
255
def assertNotShown(self, notification):
256
"""Assert that the notification was shown."""
257
self.assertNotIn(notification,
258
self.toggleable.notification.notifications_shown)
260
def test_send_notification_passes_thru(self):
261
"""The send_notification method passes thru."""
263
self.toggleable.send_notification(*args)
264
self.assertShown(args)
266
def test_update_notification_passes_thru(self):
267
"""The update_notification method passes thru."""
268
show_args = ("title", "body", "icon", "append")
269
update_args = ("other title", "other body")
270
expected = update_args + show_args[2:]
271
notification_id = self.toggleable.send_notification(*show_args)
272
self.toggleable.update_notification(notification_id, *update_args)
273
self.assertShown(expected)
275
def test_send_notification_honored_when_enabled(self):
276
"""The send_notification method is honored when enabled."""
277
self.notification_switch.enable_notifications()
278
args = (aggregator.UBUNTUONE_TITLE, "hello", None, False)
279
self.toggleable.send_notification(*args)
280
self.assertShown(args)
282
def test_send_notification_ignored_when_disabled(self):
283
"""The send_notification method is ignored when disabled."""
284
self.notification_switch.disable_notifications()
285
args = (aggregator.UBUNTUONE_TITLE, "hello", None, False)
286
self.toggleable.send_notification(*args)
287
self.assertNotShown(args)
289
def test_update_notification_honored_when_enabled(self):
290
"""The update_notification method is honored when enabled."""
291
self.notification_switch.enable_notifications()
292
show_args = ("title", "body", "icon", "append")
293
update_args = ("other title", "other body")
294
expected = update_args + show_args[2:]
295
notification_id = self.toggleable.send_notification(*show_args)
296
self.toggleable.update_notification(notification_id, *update_args)
297
self.assertShown(expected)
299
def test_update_notification_ignored_when_disabled(self):
300
"""The update_notification method is ignored when disabled."""
301
self.notification_switch.disable_notifications()
302
show_args = ("title", "body", "icon", "append")
303
update_args = ("other title", "other body")
304
expected = update_args + show_args[2:]
305
notification_id = self.toggleable.send_notification(*show_args)
306
self.toggleable.update_notification(notification_id, *update_args)
307
self.assertNotShown(expected)
309
def test_update_after_disabled_show(self):
310
"""An update is ignored if the corresponding show was disabled."""
311
self.notification_switch.disable_notifications()
312
show_args = ("title", "body", "icon", "append")
313
update_args = ("other title", "other body")
314
expected = update_args + show_args[2:]
315
notification_id = self.toggleable.send_notification(*show_args)
316
self.notification_switch.enable_notifications()
317
self.toggleable.update_notification(notification_id, *update_args)
318
self.assertNotShown(expected)
321
class NotificationSwitchTestCase(TestCase):
322
"""Test the NotificationSwitch class."""
325
"""Initialize this test instance."""
326
self.notification_switch = aggregator.NotificationSwitch()
328
def test_build_notification(self):
329
"""A new notification instance is returned."""
330
notification = self.notification_switch.build_notification()
331
self.assertEqual(notification.notification_switch,
332
self.notification_switch)
334
def test_enable_notifications(self):
335
"""The switch is turned on."""
336
self.notification_switch.enable_notifications()
337
self.assertTrue(self.notification_switch.enabled)
339
def test_disable_notifications(self):
340
"""The switch is turned off."""
341
self.notification_switch.disable_notifications()
342
self.assertFalse(self.notification_switch.enabled)
223
345
class FileDiscoveryBubbleTestCase(TestCase):
224
346
"""Test the FileDiscoveryBubble class."""
227
349
"""Initialize this test instance."""
228
self.patch(aggregator, "Notification", FakeNotificationSingleton())
350
self.patch(aggregator, "ToggleableNotification",
351
FakeNotificationSingleton())
229
352
self.clock = PatchedClock()
230
353
self.aggregator = FakeStatusAggregator(clock=self.clock)
231
354
self.bubble = aggregator.FileDiscoveryBubble(self.aggregator,
483
608
self.assertEqual(1, len(self.bubble.notification.notifications_shown))
611
class FakeLauncher(object):
612
"""A fake UbuntuOneLauncher."""
614
progress_visible = False
616
emblem_visible = False
619
def show_progressbar(self):
620
"""The progressbar is shown."""
621
self.progress_visible = True
623
def hide_progressbar(self):
624
"""The progressbar is hidden."""
625
self.progress_visible = False
627
def set_progress(self, value):
628
"""The progressbar value is changed."""
629
self.progress = value
631
def show_warning_emblem(self):
632
"""Show a warning emblem."""
633
self.emblem_visible = True
635
def hide_emblem(self):
636
"""Hide the current emblem."""
637
self.emblem_visible = False
640
class FakeInhibitor(object):
641
"""A fake session inhibitor."""
643
def inhibit(self, flags, reason):
644
"""Inhibit some events with a given reason."""
646
return defer.succeed(self)
649
"""Cancel the inhibition for the current cookie."""
651
return defer.succeed(self)
486
654
class ProgressBarTestCase(TestCase):
487
655
"""Tests for the progress bar."""
490
658
"""Initialize this test instance."""
659
self.patch(aggregator, "UbuntuOneLauncher", FakeLauncher)
660
self.patch(aggregator.session, "Inhibitor", FakeInhibitor)
491
661
self.clock = PatchedClock()
492
662
self.bar = aggregator.ProgressBar(clock=self.clock)
493
663
self.addCleanup(self.bar.cleanup)
520
691
self.bar.progress_made(50, 100)
521
692
self.assertNotEqual(self.bar.timer, None)
694
def test_cleanup_resets_timer(self):
695
"""The cleanup method resets the timer."""
696
self.bar.progress_made(50, 100)
698
self.assertEqual(self.bar.timer, None)
523
700
def test_progress_made_not_updated_initially(self):
524
701
"""Progress made is not updated initially."""
525
702
self.bar.progress_made(50, 100)
526
703
self.assertEqual(0, len(self.timeout_calls))
704
self.assertEqual(0.0, self.bar.launcher.progress)
528
706
def test_progress_made_updated_after_a_delay(self):
529
707
"""The progressbar is updated after a delay."""
530
708
self.bar.progress_made(50, 100)
531
709
self.clock.advance(aggregator.ProgressBar.updates_delay)
532
self.assertIn(50.0, self.timeout_calls)
710
self.assertIn(0.5, self.timeout_calls)
711
self.assertEqual(0.5, self.bar.launcher.progress)
534
713
def test_progress_updates_are_aggregated(self):
535
714
"""The progressbar is updated after a delay."""
552
733
self.bar.progress_made(50, 100)
553
734
self.bar.completed()
554
735
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)
736
self.assertFalse(self.bar.launcher.progress_visible)
738
@defer.inlineCallbacks
739
def test_progress_made_inhibits_logout_suspend(self):
740
"""Suspend and logout are inhibited when the progressbar is shown."""
741
self.bar.progress_made(50, 100)
742
expected = aggregator.session.INHIBIT_LOGOUT_SUSPEND
743
inhibitor = yield self.bar.inhibitor_defer
744
self.assertEqual(inhibitor.flags, expected)
746
@defer.inlineCallbacks
747
def test_completed_uninhibits_logout_suspend(self):
748
"""Suspend and logout are uninhibited when all has completed."""
749
self.bar.progress_made(50, 100)
750
d = self.bar.inhibitor_defer
753
self.assertEqual(inhibitor.flags, 0)
755
def test_show_warning_emblem(self):
756
"""The warning emblem is shown."""
757
self.bar.hide_emblem()
758
self.bar.show_warning_emblem()
759
self.assertTrue(self.bar.emblem_visible)
760
self.assertTrue(self.bar.launcher.emblem_visible)
762
def test_hide_emblem(self):
763
"""The emblem is hidden."""
764
self.bar.show_warning_emblem()
765
self.bar.hide_emblem()
766
self.assertFalse(self.bar.emblem_visible)
767
self.assertFalse(self.bar.launcher.emblem_visible)
569
770
class FakeDelayedBuffer(object):
623
826
"""A new command was unqueued."""
624
827
self.queued_commands.discard(command)
829
def build_notification(self):
830
"""Create a new toggleable notification object."""
831
return self.notification_switch.build_notification()
626
833
download_started = misc_command_queued
627
834
download_finished = misc_command_unqueued
628
835
upload_started = misc_command_queued
629
836
upload_finished = misc_command_unqueued
632
class StatusFrontendTestCase(TestCase):
838
def connection_made(self):
839
"""The client made the connection to the server."""
840
self.connected = True
842
def connection_lost(self):
843
"""The client lost the connection to the server."""
844
self.connected = False
847
class StatusFrontendTestCase(BaseTwistedTestCase):
633
848
"""Test the status frontend."""
636
851
"""Initialize this test instance."""
852
BaseTwistedTestCase.setUp(self)
637
853
self.patch(aggregator, "StatusAggregator", FakeAggregator)
638
self.patch(aggregator, "Notification", FakeNotificationSingleton())
854
self.patch(aggregator, "ToggleableNotification",
855
FakeNotificationSingleton())
639
856
self.patch(aggregator, "Messaging", FakeMessaging)
640
857
self.fakefsm = None
641
858
self.fakevm = FakeVolumeManager()
824
1044
(aggregator.UBUNTUONE_TITLE,
825
1045
aggregator.ConnectionMadeStatus.MESSAGE_ONE, None, False),
826
1046
self.status_frontend.notification.notifications_shown[0])
1047
self.assertTrue(self.status_frontend.aggregator.connected)
1049
def test_set_show_all_notifications(self):
1050
"""Test the set_show_all_notifications method."""
1051
self.status_frontend.set_show_all_notifications(False)
1052
self.assertFalse(self.status_frontend.aggregator.
1053
notification_switch.enabled)
829
1056
class StatusEventTestCase(TestCase):
830
1057
"""Test the status event class and children."""
832
1059
CLASS = aggregator.StatusEvent
836
1063
def setUp(self):
837
1064
"""Initialize this test instance."""
838
1065
if type(self) == StatusEventTestCase:
839
self.assertRaises(AssertionError, self.CLASS, *self.CLASS_ARGS)
1066
self.assertRaises(AssertionError, self.CLASS, **self.CLASS_KWARGS)
841
self.status = self.CLASS(*self.CLASS_ARGS)
1068
self.status = self.CLASS(**self.CLASS_KWARGS)
843
1070
def test_one_message_defined(self):
844
1071
"""The singular message is defined as MESSAGE_ONE."""
1117
1352
self.aggregator.download_total = 8
1118
1353
self.aggregator.done_counter = 9
1119
1354
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))
1355
percentage = int(100.0 * self.aggregator.done_counter /
1356
self.aggregator.total_counter)
1358
"total_uploading_files": self.aggregator.upload_total,
1359
"total_downloading_files": self.aggregator.download_total,
1360
"percentage_completed": percentage,
1363
format_string = (aggregator.FILES_UPLOADING + "\n" +
1364
aggregator.FILES_DOWNLOADING + "\n" +
1365
aggregator.PROGRESS_COMPLETED)
1367
expected = format_string % format_args
1123
1368
result = self.aggregator.get_progress_message()
1124
1369
self.assertEqual(expected, result)
1187
1472
self.aggregator.restart_progress_bubble()
1188
1473
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)
1475
def test_queue_done_shows_bubble_when_downloads_happened(self):
1476
"""On queue done, show final bubble if downloads happened."""
1478
self.status_frontend.download_started(fc)
1479
self.status_frontend.download_finished(fc)
1480
old_final_bubble = self.aggregator.final_status_bubble
1481
self.aggregator.queue_done()
1482
self.assertTrue(old_final_bubble.shown)
1484
def test_queue_done_shows_bubble_when_uploads_happened(self):
1485
"""On queue done, show final bubble if uploads happened."""
1487
self.status_frontend.upload_started(fc)
1488
self.status_frontend.upload_finished(fc)
1489
old_final_bubble = self.aggregator.final_status_bubble
1490
self.aggregator.queue_done()
1491
self.assertTrue(old_final_bubble.shown)
1493
def test_queue_done_does_not_show_bubble_when_no_transfers_happened(self):
1494
"""On queue done, don't show final bubble if no transfers happened."""
1496
self.status_frontend.upload_started(fc)
1497
old_final_bubble = self.aggregator.final_status_bubble
1498
self.aggregator.queue_done()
1499
self.assertFalse(old_final_bubble.shown)
1501
def test_queue_done_resets_status_and_hides_progressbar(self):
1502
"""On queue done, reset counters and hide progressbar."""
1504
self.status_frontend.upload_started(fc)
1505
self.aggregator.queue_done()
1197
1506
self.assertStatusReset()
1198
self.assertEqual(0.0, self.aggregator.progress_bar.percentage)
1507
self.assertEqual(0.0, self.aggregator.progress_bar.progress)
1199
1508
self.assertFalse(self.aggregator.progress_bar.visible)
1510
def test_connection_lost(self):
1511
"""The connection to the server was lost."""
1512
self.status_frontend.server_connection_lost()
1513
self.assertTrue(self.aggregator.progress_bar.emblem_visible)
1515
def test_connection_made(self):
1516
"""The connection to the server was made."""
1517
self.status_frontend.server_connection_made()
1518
self.assertFalse(self.aggregator.progress_bar.emblem_visible)
1202
1521
class StatusGrouperTestCase(TestCase):
1203
1522
"""Tests for the group_statuses function."""
1274
1598
clock.advance(aggregator.ProgressBubble.sleep_delay * 2)
1275
1599
# no more notifications are shown
1276
1600
self.assertEqual(5, len(notifications_shown))
1602
def test_all_together_now_off(self):
1603
"""Make all parts work together, but with notifications off."""
1604
self.patch(aggregator, "ToggleableNotification",
1605
FakeNotificationSingleton())
1606
self.patch(aggregator, "UbuntuOneLauncher", FakeLauncher)
1607
self.patch(aggregator.session, "Inhibitor", FakeInhibitor)
1608
clock = PatchedClock()
1609
upload = FakeCommand()
1610
sf = aggregator.StatusFrontend(clock=clock)
1611
sf.set_show_all_notifications(False)
1613
clock.advance(aggregator.ProgressBubble.sleep_delay)
1614
# the progress bar is not visible yet
1615
self.assertFalse(sf.aggregator.progress_bar.visible)
1616
sf.upload_started(upload)
1617
# the progress bar is now shown
1618
self.assertTrue(sf.aggregator.progress_bar.visible)
1619
notifications_shown = (sf.aggregator.file_discovery_bubble.
1620
notification.notifications_shown)
1621
# no notifications shown, never
1622
self.assertEqual(0, len(notifications_shown))
1623
clock.advance(aggregator.FileDiscoveryGatheringState.initial_delay)
1624
self.assertEqual(0, len(notifications_shown))
1625
download = FakeCommand()
1626
sf.download_started(download)
1627
self.assertEqual(0, len(notifications_shown))
1628
# the progress still is zero
1629
self.assertEqual(0.0, sf.aggregator.progress_bar.progress)
1630
clock.advance(aggregator.FileDiscoveryUpdateState.updates_delay)
1631
self.assertEqual(0, len(notifications_shown))
1632
clock.advance(aggregator.FileDiscoveryUpdateState.updates_timeout -
1633
aggregator.FileDiscoveryUpdateState.updates_delay)
1634
sf.upload_finished(upload)
1635
# the progress bubble has no notifications yet
1636
self.assertEqual(None, sf.aggregator.progress_bubble.notification)
1637
clock.advance(aggregator.ProgressBubble.sleep_delay)
1638
self.assertEqual(0, len(notifications_shown))
1639
sf.upload_finished(download)
1640
# the progress still is now 100%
1641
self.assertEqual(1.0, sf.aggregator.progress_bar.progress)
1642
self.assertEqual(0, len(notifications_shown))
1643
clock.advance(aggregator.ProgressBubble.sleep_delay)
1644
self.assertEqual(0, len(notifications_shown))
1646
self.assertEqual(0, len(notifications_shown))
1647
clock.advance(aggregator.ProgressBubble.sleep_delay * 2)
1648
self.assertEqual(0, len(notifications_shown))