20 """Tests for Notifications"""
22 from __future__
import absolute_import
24 from unity8
import shell
28 from testtools.matchers
import Equals, NotEquals
29 from autopilot.matchers
import Eventually
31 from gi.repository
import Notify
39 logger = logging.getLogger(__name__)
43 if sys.version_info < (3,):
48 """Base class for all notification tests that provides helper methods."""
50 scenarios = _get_device_emulation_scenarios(
'Nexus4')
52 def _get_icon_path(self, icon_name):
53 """Given an icons file name returns the full path (either system or
56 Consider the graphics directory as root so for example (running tests
57 from installed unity8-autopilot package):
58 >>> self.get_icon_path('clock.png')
59 /usr/share/unity8/graphics/clock.png
61 >>> self.get_icon_path('applicationIcons/facebook.png')
62 /usr/share/unity8/graphics/applicationIcons/facebook.png
65 if os.path.abspath(__file__).startswith(
'/usr/'):
66 return '/usr/share/unity8/graphics/' + icon_name
68 return os.path.dirname(__file__) + (
69 "/../../../../../qml/graphics/" + icon_name)
71 def _get_notifications_list(self):
72 return self.main_window.select_single(
74 objectName=
'notificationList'
77 def _assert_notification(
86 """Assert that the expected qualities of a notification are as
91 if summary
is not None:
92 self.assertThat(notification.summary, Eventually(Equals(summary)))
95 self.assertThat(notification.body, Eventually(Equals(body)))
98 self.assertThat(notification.iconSource, Eventually(NotEquals(
"")))
100 self.assertThat(notification.iconSource, Eventually(Equals(
"")))
104 notification.secondaryIconSource,
105 Eventually(NotEquals(
""))
109 notification.secondaryIconSource,
110 Eventually(Equals(
""))
113 if opacity
is not None:
114 self.assertThat(notification.opacity, Eventually(Equals(opacity)))
118 """Collection of test for Interactive tests including snap decisions."""
121 super(InteractiveNotificationBase, self).setUp()
126 """Interactive notification must react upon click on itself."""
128 unlock_unity(unity_proxy)
132 summary =
"Interactive notification"
133 body =
"This notification can be clicked on to trigger an action."
135 actions = [(
"action_id",
"dummy")]
137 (
"x-canonical-switch-to-application",
"true"),
139 "x-canonical-secondary-icon",
153 get_notification =
lambda: notify_list.wait_select_single(
154 'Notification', objectName=
'notification1')
155 notification = get_notification()
157 self.touch.tap_object(
158 notification.select_single(objectName=
"interactiveArea")
164 """Rejecting a call should make notification expand and
165 offer more options."""
167 unlock_unity(unity_proxy)
169 summary =
"Incoming call"
170 body =
"Frank Zappa\n+44 (0)7736 027340"
174 "x-canonical-secondary-icon",
177 (
"x-canonical-snap-decisions",
"true"),
181 (
'action_accept',
'Accept'),
182 (
'action_decline_1',
'Decline'),
183 (
'action_decline_2',
'"Can\'t talk now, what\'s up?"'),
184 (
'action_decline_3',
'"I call you back."'),
185 (
'action_decline_4',
'Send custom message...'),
198 get_notification =
lambda: notify_list.wait_select_single(
199 'Notification', objectName=
'notification1')
200 notification = get_notification()
202 initial_height = notification.height
203 self.touch.tap_object(notification.select_single(objectName=
"button1"))
206 Eventually(Equals(initial_height +
207 3 * notification.select_single(
208 objectName=
"buttonColumn").spacing +
209 3 * notification.select_single(
210 objectName=
"button4").height)))
211 self.touch.tap_object(notification.select_single(objectName=
"button4"))
215 """A snap-decision on a phone should block input to shell beneath it when there's no greeter."""
217 unlock_unity(unity_proxy)
219 summary =
"Incoming file"
220 body =
"Frank would like to send you the file: essay.pdf"
221 icon_path =
"sync-idle"
223 (
"x-canonical-snap-decisions",
"true"),
224 (
"x-canonical-non-shaped-icon",
"true"),
228 (
'action_accept',
'Accept'),
229 (
'action_decline_1',
'Decline'),
243 self.main_window.show_dash_swiping()
244 launcher = self.main_window.get_launcher()
245 self.assertThat(launcher.shown, Eventually(Equals(
False)))
249 get_notification =
lambda: notify_list.wait_select_single(
250 'Notification', objectName=
'notification1')
251 notification = get_notification()
253 self.touch.tap_object(notification.select_single(objectName=
"button0"))
257 """A snap-decision on a phone should not block input to the greeter beneath it."""
260 summary =
"Incoming file"
261 body =
"Frank would like to send you the file: essay.pdf"
262 icon_path =
"sync-idle"
264 (
"x-canonical-snap-decisions",
"true"),
265 (
"x-canonical-non-shaped-icon",
"true"),
269 (
'action_accept',
'Accept'),
270 (
'action_decline_1',
'Decline'),
284 self.main_window.show_dash_swiping()
285 greeter = self.main_window.get_greeter()
286 self.assertThat(greeter.shown, Eventually(Equals(
False)))
290 get_notification =
lambda: notify_list.wait_select_single(
291 'Notification', objectName=
'notification1')
292 notification = get_notification()
294 self.touch.tap_object(notification.select_single(objectName=
"button0"))
297 def _create_interactive_notification(
306 """Create a interactive notification command.
308 :param summary: Summary text for the notification
309 :param body: Body text to display in the notification
310 :param icon: Path string to the icon to use
311 :param urgency: Urgency string for the noticiation, either: 'LOW',
313 :param actions: List of tuples containing the 'id' and 'label' for all
315 :param hint_strings: List of tuples containing the 'name' and value for
316 setting the hint strings for the notification
321 "Creating snap-decision notification with summary(%s), body(%s) "
329 '--summary', summary,
335 script_args.extend([
'--icon', icon])
339 script_args.extend([
'--hint',
"%s,%s" % (key, value)])
341 for action
in actions:
342 action_id, action_label = action
343 action_string =
"%s,%s" % (action_id, action_label)
344 script_args.extend([
'--action', action_string])
346 python_bin = subprocess.check_output([
'which',
'python']).strip()
348 logger.info(
"Launching snap-decision notification as: %s", command)
351 stdin=subprocess.PIPE,
352 stdout=subprocess.PIPE,
353 stderr=subprocess.PIPE,
355 universal_newlines=
True,
360 poll_result = self._notify_proc.poll()
361 if poll_result
is not None and self._notify_proc.returncode != 0:
362 error_output = self._notify_proc.communicate()[1]
363 raise RuntimeError(
"Call to script failed with: %s" % error_output)
365 def _get_notify_script(self):
366 """Returns the path to the interactive notification creation script."""
367 file_path =
"../../emulators/create_interactive_notification.py"
369 the_path = os.path.abspath(
370 os.path.join(__file__, file_path))
374 def _tidy_up_script_process(self):
375 if self.
_notify_proc is not None and self._notify_proc.poll()
is None:
376 logger.error(
"Notification process wasn't killed, killing now.")
377 os.killpg(self._notify_proc.pid, signal.SIGTERM)
380 """Assert that the interactive notification callback of id *action_id*
383 :raises AssertionError: If no interactive notification has actually
385 :raises AssertionError: When *action_id* does not match the actual
387 :raises AssertionError: If no callback was called at all.
391 raise AssertionError(
"No interactive notification was created.")
393 for i
in range(timeout):
394 self._notify_proc.poll()
395 if self._notify_proc.returncode
is not None:
396 output = self._notify_proc.communicate()
397 actual_action_id = output[0].strip(
"\n")
398 if actual_action_id != action_id:
399 raise AssertionError(
400 "action id '%s' does not match actual returned '%s'"
401 % (action_id, actual_action_id)
407 os.killpg(self._notify_proc.pid, signal.SIGTERM)
409 raise AssertionError(
410 "No callback was called, killing interactivenotification script"
415 """Collection of tests for Emphemeral notifications (non-interactive.)"""
418 super(EphemeralNotificationsTests, self).setUp()
421 Notify.init(
"Autopilot Ephemeral Notification Tests")
422 self.addCleanup(Notify.uninit)
425 """Notification must display the expected summary and body text."""
427 unlock_unity(unity_proxy)
431 summary =
"Icon-Summary-Body"
432 body =
"Hey pal, what's up with the party next weekend? Will you " \
437 "x-canonical-secondary-icon",
442 notification = shell.create_ephemeral_notification(
452 notification =
lambda: notify_list.wait_select_single(
453 'Notification', objectName=
'notification1')
464 """Notification must display the expected summary and secondary
467 unlock_unity(unity_proxy)
471 summary =
"Upload of image completed"
474 "x-canonical-secondary-icon",
479 notification = shell.create_ephemeral_notification(
489 notification =
lambda: notify_list.wait_select_single(
490 'Notification', objectName=
'notification1')
501 """Notifications must be displayed in order according to their
504 unlock_unity(unity_proxy)
508 summary_low =
'Low Urgency'
509 body_low =
"No, I'd rather see paint dry, pal *yawn*"
512 summary_normal =
'Normal Urgency'
513 body_normal =
"Hey pal, what's up with the party next weekend? Will " \
514 "you join me and Anna?"
517 summary_critical =
'Critical Urgency'
518 body_critical =
'Dude, this is so urgent you have no idea :)'
519 icon_path_critical = self.
_get_icon_path(
'avatars/anna_olsson.png')
521 notification_normal = shell.create_ephemeral_notification(
527 notification_normal.show()
529 notification_low = shell.create_ephemeral_notification(
535 notification_low.show()
537 notification_critical = shell.create_ephemeral_notification(
543 notification_critical.show()
545 get_notification =
lambda: notify_list.wait_select_single(
547 summary=summary_critical
550 notification = get_notification()
560 get_normal_notification =
lambda: notify_list.wait_select_single(
562 summary=summary_normal
564 notification = get_normal_notification()
574 get_low_notification =
lambda: notify_list.wait_select_single(
578 notification = get_low_notification()
589 """Notification must display the expected summary- and body-text."""
591 unlock_unity(unity_proxy)
595 summary =
'Summary-Body'
596 body =
'This is a superfluous notification'
598 notification = shell.create_ephemeral_notification(summary, body)
601 notification = notify_list.wait_select_single(
602 'Notification', objectName=
'notification1')
614 """Notification must display only the expected summary-text."""
616 unlock_unity(unity_proxy)
620 summary =
'Summary-Only'
622 notification = shell.create_ephemeral_notification(summary)
625 notification = notify_list.wait_select_single(
626 'Notification', objectName=
'notification1')
631 """Notification must allow updating its contents while being
634 unlock_unity(unity_proxy)
638 summary =
'Initial notification'
639 body =
'This is the original content of this notification-bubble.'
642 notification = shell.create_ephemeral_notification(
649 get_notification =
lambda: notify_list.wait_select_single(
650 'Notification', summary=summary)
660 summary =
'Updated notification'
661 body =
'Here the same bubble with new title- and body-text, even ' \
662 'the icon can be changed on the update.'
664 notification.update(summary, body, icon_path)
669 """Notification must allow updating its contents and layout while
672 unlock_unity(unity_proxy)
676 summary =
'Initial layout'
677 body =
'This bubble uses the icon-title-body layout with a ' \
682 notification = shell.create_ephemeral_notification(
687 notification.set_hint_string(
688 'x-canonical-secondary-icon',
693 get_notification =
lambda: notify_list.wait_select_single(
694 'Notification', objectName=
'notification1')
705 notification.clear_hints()
706 summary =
'Updated layout'
707 body =
'After the update we now have a bubble using the title-body ' \
709 notification.update(summary, body,
None)
712 self.assertThat(get_notification, Eventually(NotEquals(
None)))
716 """Notification has to accumulate body-text using append-hint."""
718 unlock_unity(unity_proxy)
722 summary =
'Cole Raby'
723 body =
'Hey Bro Coly!'
726 notification = shell.create_ephemeral_notification(
730 hints=[(
'x-canonical-append',
'true')]
735 get_notification =
lambda: notify_list.wait_select_single(
736 'Notification', objectName=
'notification1')
738 notification = get_notification()
750 'Did you watch the air-race in Oshkosh last week?',
751 'Phil owned the place like no one before him!',
752 'Did really everything in the race work according to regulations?',
753 'Somehow I think to remember Burt Williams did cut corners and '
754 'was not punished for this.',
755 'Hopefully the referees will watch the videos of the race.',
756 'Burt could get fined with US$ 50000 for that rule-violation :)'
759 for new_body
in bodies:
761 body_sum +=
'\n' + body
762 notification = shell.create_ephemeral_notification(
766 hints=[(
'x-canonical-append',
'true')]
770 get_notification =
lambda: notify_list.wait_select_single(
772 objectName=
'notification1'
774 notification = get_notification()
def test_update_notification_same_layout
def _get_notifications_list
def test_modal_sd_without_greeter
def assert_notification_action_id_was_called
def _tidy_up_script_process
def _create_interactive_notification
def test_summary_and_body
def test_sd_incoming_call
def test_icon_summary_body
def test_update_notification_layout_change
def test_modal_sd_with_greeter