diff -u unity-7.4.0+16.04.20151218/debian/changelog unity-7.4.0+16.04.20151218/debian/changelog --- unity-7.4.0+16.04.20151218/debian/changelog +++ unity-7.4.0+16.04.20151218/debian/changelog @@ -1,3 +1,9 @@ +unity (7.4.0+16.04.20151218-0ubuntu2) xenial; urgency=medium + + * Multiarchify the library packages. + + -- Matthias Klose Thu, 28 Jan 2016 17:38:52 +0100 + unity (7.4.0+16.04.20151218-0ubuntu1) xenial; urgency=medium [ Aneesh Madhavan ] diff -u unity-7.4.0+16.04.20151218/debian/control unity-7.4.0+16.04.20151218/debian/control --- unity-7.4.0+16.04.20151218/debian/control +++ unity-7.4.0+16.04.20151218/debian/control @@ -139,6 +139,8 @@ Package: libunity-core-6.0-9 Section: libs Architecture: any +Multi-Arch: same +Pre-Depends: ${misc:Pre-Depends} Depends: ${shlibs:Depends}, ${misc:Depends}, unity-services (= ${binary:Version}), @@ -159,6 +161,7 @@ Package: libunity-core-6.0-dev Section: libdevel Architecture: any +Multi-Arch: same Depends: ${misc:Depends}, libunity-core-6.0-9 (= ${binary:Version}), libglib2.0-dev, diff -u unity-7.4.0+16.04.20151218/debian/libunity-core-6.0-9.install unity-7.4.0+16.04.20151218/debian/libunity-core-6.0-9.install --- unity-7.4.0+16.04.20151218/debian/libunity-core-6.0-9.install +++ unity-7.4.0+16.04.20151218/debian/libunity-core-6.0-9.install @@ -1,4 +1,4 @@ -usr/lib/libunity-core*.so.* +usr/lib/*/libunity-core*.so.* usr/lib/unity/*.py usr/share/ccsm usr/share/gnome-control-center/ diff -u unity-7.4.0+16.04.20151218/debian/libunity-core-6.0-dev.install unity-7.4.0+16.04.20151218/debian/libunity-core-6.0-dev.install --- unity-7.4.0+16.04.20151218/debian/libunity-core-6.0-dev.install +++ unity-7.4.0+16.04.20151218/debian/libunity-core-6.0-dev.install @@ -2,2 +2,2 @@ -usr/lib/pkgconfig/ -usr/lib/libunity-core*.so +usr/lib/*/pkgconfig/ +usr/lib/*/libunity-core*.so diff -u unity-7.4.0+16.04.20151218/debian/rules unity-7.4.0+16.04.20151218/debian/rules --- unity-7.4.0+16.04.20151218/debian/rules +++ unity-7.4.0+16.04.20151218/debian/rules @@ -42,6 +42,9 @@ rm -f debian/tmp/usr/share/compiz/networkarearegion.xml rm -f debian/tmp//usr/lib/compiz/libnetworkarearegion.so rm -rf debian/tmp/usr/share/gconf/schemas/ + mkdir -p debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH) + mv debian/tmp/usr/lib/lib*so* debian/tmp/usr/lib/pkgconfig \ + debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH) dh_install --fail-missing override_dh_gencontrol: only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/X11.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/X11.py @@ -0,0 +1,117 @@ +from __future__ import absolute_import + +from autopilot.utilities import Silence +from autopilot.display import Display +from autopilot.input import Mouse, Keyboard +from autopilot.process import Window + +import logging +import subprocess +import os +from Xlib import X, display, protocol +from gi.repository import Gdk + + +logger = logging.getLogger(__name__) + +_display = None + +_blacklisted_drivers = ["NVIDIA"] + +def _get_display(): + """Get a Xlib display object. Creating the display prints garbage to stdout.""" + global _display + if _display is None: + with Silence(): + _display = display.Display() + return _display + +def _getProperty(_type, win=None): + if not win: + win = _get_display().screen().root + atom = win.get_full_property(_get_display().get_atom(_type), X.AnyPropertyType) + if atom: return atom.value + +def get_desktop_viewport(): + """Get the x,y coordinates for the current desktop viewport top-left corner.""" + return _getProperty('_NET_DESKTOP_VIEWPORT') + + +# Note: this use to exist within autopilot, moved here due to Autopilot 1.3 +# upgrade. +def drag_window_to_screen(window, screen): + """Drags *window* to *screen* + + :param autopilot.process.Window window: The window to drag + :param integer screen: The screen to drag the *window* to + :raises: **TypeError** if *window* is not a autopilot.process.Window + + """ + if not isinstance(window, Window): + raise TypeError("Window must be a autopilot.process.Window") + + if window.monitor == screen: + logger.debug("Window %r is already on screen %d." % (window.x_id, screen)) + return + + assert(not window.is_maximized) + (win_x, win_y, win_w, win_h) = window.geometry + (mx, my, mw, mh) = Display.create().get_screen_geometry(screen) + + logger.debug("Dragging window %r to screen %d." % (window.x_id, screen)) + + mouse = Mouse.create() + keyboard = Keyboard.create() + mouse.move(win_x + win_w/2, win_y + win_h/2) + keyboard.press("Alt") + mouse.press() + keyboard.release("Alt") + + # We do the movements in two steps, to reduce the risk of being + # blocked by the pointer barrier + target_x = mx + mw/2 + target_y = my + mh/2 + mouse.move(win_x, target_y, rate=20, time_between_events=0.005) + mouse.move(target_x, target_y, rate=20, time_between_events=0.005) + mouse.release() + + +# Note: this use to exist within autopilot, moved here due to Autopilot 1.3 +# upgrade. +def set_primary_monitor(monitor): + """Set *monitor* to be the primary monitor. + + :param int monitor: Must be between 0 and the number of configured + monitors. + :raises: **ValueError** if an invalid monitor is specified. + :raises: **BlacklistedDriverError** if your video driver does not + support this. + + """ + try: + glxinfo_out = subprocess.check_output("glxinfo") + except OSError, e: + raise OSError("Failed to run glxinfo: %s. (do you have mesa-utils installed?)" % e) + + for dri in _blacklisted_drivers: + if dri in glxinfo_out: + raise Display.BlacklistedDriverError('Impossible change the primary monitor for the given driver') + + num_monitors = Display.create().get_num_screens() + if monitor < 0 or monitor >= num_monitors: + raise ValueError('Monitor %d is not in valid range of 0 <= monitor < %d.' % (num_monitors)) + + default_screen = Gdk.Screen.get_default() + monitor_name = default_screen.get_monitor_plug_name(monitor) + + if not monitor_name: + raise ValueError('Could not get monitor name from monitor number %d.' % (monitor)) + + ret = os.spawnlp(os.P_WAIT, "xrandr", "xrandr", "--output", monitor_name, "--primary") + + if ret != 0: + raise RuntimeError('Xrandr can\'t set the primary monitor. error code: %d' % (ret)) + +def reset_display(): + from autopilot.input._X11 import reset_display + reset_display() only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/__init__.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/__init__.py @@ -0,0 +1,77 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +"""A collection of Unity-specific emulators.""" + +from time import sleep + +from autopilot.introspection import ( + get_proxy_object_for_existing_process, + ProcessSearchError + ) +from autopilot.introspection.dbus import CustomEmulatorBase +from autopilot.introspection.backends import DBusAddress + +from dbus import DBusException + + +class UnityIntrospectionObject(CustomEmulatorBase): + + DBUS_SERVICE = "com.canonical.Unity" + DBUS_OBJECT = "/com/canonical/Unity/Debug" + + _Backend = DBusAddress.SessionBus(DBUS_SERVICE, DBUS_OBJECT) + + def _repr_string(self, obj_details=""): + geostr = "" + if hasattr(self, 'globalRect'): + geostr = " geo=[{r.x}x{r.y} {r.width}x{r.height}]".format(r=self.globalRect) + + obj_details.strip() + obj_details = " "+obj_details if len(obj_details) else "" + + return "<{cls} {addr} id={id}{geo}{details}>".format(cls=self.__class__.__name__, + addr=hex(id(self)), + id=self.id, + geo=geostr, + details=obj_details) + + def __repr__(self): + with self.no_automatic_refreshing(): + return self._repr_string() + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.id == other.id + + def __ne__(self, other): + return not self.__eq__(other) + + +def ensure_unity_is_running(timeout=300): + """Poll the unity debug interface, and return when it's ready for use. + + The default timeout is 300 seconds (5 minutes) to account for the case where + Unity has crashed and is taking a while to get restarted (on a slow VM for + example). + + If, after the timeout period, unity is still not up, this function raises a + RuntimeError exception. + + """ + sleep_period=10 + for i in range(0, timeout, sleep_period): + try: + get_proxy_object_for_existing_process( + connection_name=UnityIntrospectionObject.DBUS_SERVICE, + object_path=UnityIntrospectionObject.DBUS_OBJECT + ) + return True + except ProcessSearchError: + sleep(sleep_period) + raise RuntimeError("Unity debug interface is down after %d seconds of polling." % (timeout)) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/compiz.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/compiz.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import + +"""Functions that wrap compizconfig to avoid some unpleasantness in that module.""" + +from __future__ import absolute_import + +from autopilot.utilities import Silence + +_global_context = None + +def get_global_context(): + """Get the compizconfig global context object.""" + global _global_context + if _global_context is None: + with Silence(): + from compizconfig import Context + _global_context = Context() + return _global_context + + +def _get_plugin(plugin_name): + """Get a compizconfig plugin with the specified name. + + Raises KeyError of the plugin named does not exist. + + """ + ctx = get_global_context() + with Silence(): + try: + return ctx.Plugins[plugin_name] + except KeyError: + raise KeyError("Compiz plugin '%s' does not exist." % (plugin_name)) + + +def get_compiz_setting(plugin_name, setting_name): + """Get a compiz setting object. + + *plugin_name* is the name of the plugin (e.g. 'core' or 'unityshell') + *setting_name* is the name of the setting (e.g. 'alt_tab_timeout') + + :raises: KeyError if the plugin or setting named does not exist. + + :returns: a compiz setting object. + + """ + plugin = _get_plugin(plugin_name) + with Silence(): + try: + return plugin.Screen[setting_name] + except KeyError: + raise KeyError("Compiz setting '%s' does not exist in plugin '%s'." % (setting_name, plugin_name)) + + +def get_compiz_option(plugin_name, setting_name): + """Get a compiz setting value. + + This is the same as calling: + + >>> get_compiz_setting(plugin_name, setting_name).Value + + """ + return get_compiz_setting(plugin_name, setting_name).Value + only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/dash.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/dash.py @@ -0,0 +1,568 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +from autopilot.input import Keyboard, Mouse + +from autopilot.keybindings import KeybindingsHelper +from testtools.matchers import GreaterThan + +from unity.emulators.panel import WindowButtons + +from unity.emulators import UnityIntrospectionObject +import logging +import dbus + +logger = logging.getLogger(__name__) + + +class DashController(UnityIntrospectionObject, KeybindingsHelper): + """The main dash controller object.""" + + def __init__(self, *args, **kwargs): + super(DashController, self).__init__(*args, **kwargs) + self.keyboard = Keyboard.create() + self.mouse = Mouse.create() + + def get_dash_view(self): + """Get the dash view that's attached to this controller.""" + return self.get_children_by_type(DashView)[0] + + def hide_dash_via_dbus(self): + """ Emulate a DBus call for dash hiding """ + dash_object = dbus.SessionBus().get_object('com.canonical.Unity', + '/com/canonical/Unity/Dash') + dash_iface = dbus.Interface(dash_object, 'com.canonical.Unity.Dash') + dash_iface.HideDash() + + @property + def view(self): + return self.get_dash_view() + + def toggle_reveal(self): + """ + Reveals the dash if it's currently hidden, hides it otherwise. + """ + old_state = self.visible + logger.debug("Toggling dash visibility with Super key.") + self.keybinding("dash/reveal", 0.1) + self.visible.wait_for(not old_state) + + def ensure_visible(self, clear_search=True): + """ + Ensures the dash is visible. + """ + if not self.visible: + self.toggle_reveal() + self.visible.wait_for(True) + if clear_search: + self.clear_search() + + def ensure_hidden(self): + """ + Ensures the dash is hidden. + """ + if self.visible: + self.hide_dash_via_dbus() + self.visible.wait_for(False) + + @property + def search_string(self): + return self.searchbar.search_string + + @property + def searchbar(self): + """Returns the searchbar attached to the dash.""" + return self.view.get_searchbar() + + @property + def preview_displaying(self): + """Returns true if the dash is currently displaying a preview""" + return self.view.preview_displaying; + + @property + def preview_animation(self): + """Returns the average progress of dash slip and animating a preview. + Between 0.0 and 1.0. + + """ + return self.view.preview_animation; + + def get_num_rows(self): + """Returns the number of displayed rows in the dash.""" + return self.view.num_rows + + def clear_search(self): + """Clear the contents of the search bar. + + Assumes dash is already visible, and search bar has keyboard focus. + + """ + self.keyboard.press_and_release("Ctrl+a") + self.keyboard.press_and_release("Delete") + self.search_string.wait_for("") + + def reveal_application_scope(self, clear_search=True): + """Reveal the application scope.""" + logger.debug("Revealing application scope with Super+a.") + self._reveal_scope("lens_reveal/apps", clear_search) + return self.view.get_scopeview_by_name("applications.scope") + + def reveal_music_scope(self, clear_search=True): + """Reveal the music scope.""" + logger.debug("Revealing music scope with Super+m.") + self._reveal_scope("lens_reveal/music", clear_search) + return self.view.get_scopeview_by_name("music.scope") + + def reveal_file_scope(self, clear_search=True): + """Reveal the file scope.""" + logger.debug("Revealing file scope with Super+f.") + self._reveal_scope("lens_reveal/files", clear_search) + return self.view.get_scopeview_by_name("files.scope") + + def reveal_video_scope(self, clear_search=True): + """Reveal the video scope""" + logger.debug("Revealing video scope with Super+v.") + self._reveal_scope("lens_reveal/video", clear_search) + return self.view.get_scopeview_by_name("video.scope") + + def reveal_command_scope(self, clear_search=True): + """Reveal the 'run command' scope.""" + logger.debug("Revealing command scope with Alt+F2.") + self._reveal_scope("lens_reveal/command", clear_search) + return self.view.get_scopeview_by_name("commands.scope") + + def _reveal_scope(self, binding_name, clear_search): + self.keybinding_hold(binding_name) + self.keybinding_tap(binding_name) + self.keybinding_release(binding_name) + self.visible.wait_for(True) + if clear_search: + self.clear_search() + + @property + def active_scope(self): + return self.view.get_scopebar().active_scope + + def get_current_scope(self): + """Get the currently-active ScopeView object.""" + active_scope_name = self.view.get_scopebar().active_scope + return self.view.get_scopeview_by_name(active_scope_name) + + @property + def geometry(self): + return self.view.globalRect + + +class DashView(UnityIntrospectionObject): + """The dash view.""" + + def __get_window_buttons(self): + """Return the overlay window buttons view.""" + buttons = self.get_children_by_type(OverlayWindowButtons) + assert(len(buttons) == 1) + return buttons[0] + + def get_searchbar(self): + """Get the search bar attached to this dash view.""" + return self.get_children_by_type(SearchBar)[0] + + def get_scopebar(self): + """Get the scopebar attached to this dash view.""" + return self.get_children_by_type(ScopeBar)[0] + + def get_scopeview_by_name(self, scope_name): + """Get a ScopeView child object by it's name. For example, "home.scope".""" + scopes = self.get_children_by_type(ScopeView) + for scope in scopes: + if scope.name == scope_name: + return scope + + def get_preview_container(self): + """Get the preview container attached to this dash view.""" + preview_containers = self.get_children_by_type(PreviewContainer) + for preview_container in preview_containers: + return preview_container + return None + + @property + def window_buttons(self): + return self.__get_window_buttons().window_buttons() + + +class OverlayWindowButtons(UnityIntrospectionObject): + """The Overlay window buttons class""" + + def window_buttons(self): + buttons = self.get_children_by_type(WindowButtons) + assert(len(buttons) == 1) + return buttons[0] + +class SearchBar(UnityIntrospectionObject): + """The search bar for the dash view.""" + + +class ScopeBar(UnityIntrospectionObject): + """The bar of scope icons at the bottom of the dash.""" + def get_icon_by_name(self, name): + """Get a ScopeBarIcon child object by it's name. For example, 'home.scope'.""" + icons = self.get_children_by_type(ScopeBarIcon) + for icon in icons: + if icon.name == name: + return icon + +class ScopeBarIcon(UnityIntrospectionObject): + """A scope icon at the bottom of the dash.""" + + +class ScopeView(UnityIntrospectionObject): + """A Scope View.""" + + def get_categories(self, only_visible=False): + """Get a list of all groups within this scopeview. May return an empty list.""" + if only_visible: + return self.get_children_by_type(PlacesGroup, is_visible=True) + + return self.get_children_by_type(PlacesGroup) + + def get_focused_category(self): + """Return a PlacesGroup instance for the category whose header has keyboard focus. + + Returns None if no category headers have keyboard focus. + + """ + matches = self.get_children_by_type(PlacesGroup, header_has_keyfocus=True) + return matches[0] if matches else None + + def get_category_by_name(self, category_name): + """Return a PlacesGroup instance with the given name, or None.""" + matches = self.get_children_by_type(PlacesGroup, name=category_name) + return matches[0] if matches else None + + def get_num_visible_categories(self): + """Get the number of visible categories in this scope.""" + return len(self.get_categories(only_visible=True)) + + def get_filterbar(self): + """Get the filter bar for the current scope, or None if it doesn't have one.""" + bars = self.get_children_by_type(FilterBar) + return bars[0] if bars else None + + +class PlacesGroup(UnityIntrospectionObject): + """A category in the scope view.""" + + def get_results(self): + """Get a list of all results within this category. May return an empty list.""" + result_view = self.get_children_by_type(ResultView)[0] + return result_view.get_children_by_type(Result) + + +class ResultView(UnityIntrospectionObject): + """Contains a list of Result objects.""" + + +class Result(UnityIntrospectionObject): + """A single result in the dash.""" + + def activate(self, double_click=True): + m = Mouse.create() + m.click_object(self, button=1) + if double_click: + m.click_object(self, button=1) + + def preview(self, button=1): + Mouse.create().click_object(self, button) + + def preview_key(self): + Mouse.create().move_to_object(self) + + k = Keyboard.create() + k.press_and_release('Menu') + +class FilterBar(UnityIntrospectionObject): + """A filterbar, as shown inside a lens.""" + + def get_num_filters(self): + """Get the number of filters in this filter bar.""" + filters = self.get_children_by_type(FilterExpanderLabel) + return len(filters) + + def get_focused_filter(self): + """Returns the id of the focused filter widget.""" + filters = self.get_children_by_type(FilterExpanderLabel, expander_has_focus=True) + return filters[0] if filters else None + + @property + def expanded(self): + """Return True if the filterbar on this scope is expanded, False otherwise. + """ + searchbar = self._get_searchbar() + return searchbar.showing_filters + + def ensure_expanded(self): + """Expand the filter bar, if it's not already.""" + if not self.expanded: + searchbar = self._get_searchbar() + tx = searchbar.filter_label_x + (searchbar.filter_label_width / 2) + ty = searchbar.filter_label_y + (searchbar.filter_label_height / 2) + m = Mouse.create() + m.move(tx, ty) + m.click() + self.expanded.wait_for(True) + + def ensure_collapsed(self): + """Collapse the filter bar, if it's not already.""" + if self.expanded: + searchbar = self._get_searchbar() + tx = searchbar.filter_label_x + (searchbar.filter_label_width / 2) + ty = searchbar.filter_label_y + (searchbar.filter_label_height / 2) + m = Mouse.create() + m.move(tx, ty) + m.click() + self.expanded.wait_for(False) + + def _get_searchbar(self): + """Get the searchbar. + + This hack exists because there's now more than one SearchBar in Unity, + and for some reason the FilterBar stuff is bundled in the SearchBar. + + """ + searchbar_state = self.get_state_by_path("//DashView/SearchBar") + assert(len(searchbar_state) == 1) + return self.make_introspection_object(searchbar_state[0]) + + +class FilterExpanderLabel(UnityIntrospectionObject): + """A label that expands into a filter within a filter bar.""" + + def ensure_expanded(self): + """Expand the filter expander label, if it's not already""" + if not self.expanded: + Mouse.create().click_object(self) + self.expanded.wait_for(True) + + def ensure_collapsed(self): + """Collapse the filter expander label, if it's not already""" + if self.expanded: + Mouse.create().click_object(self) + self.expanded.wait_for(False) + + +class CoverArt(UnityIntrospectionObject): + """A view which can be used to show a texture, or generate one using a thumbnailer.""" + + +class RatingsButton(UnityIntrospectionObject): + """A button which shows user rating as a function of stars.""" + + +class Preview(UnityIntrospectionObject): + """A preview of a dash scope result.""" + + def get_num_actions(self): + """Get the number of actions for the preview.""" + actions = self.get_children_by_type(ActionButton) + return len(actions) + + def get_action_by_id(self, action_id): + """Returns the action given it's action hint.""" + actions = self.get_children_by_type(ActionButton, action=action_id) + return actions[0] if actions else None + + def execute_action_by_id(self, action_id): + """Executes an action given by the id.""" + action = self.get_action_by_id(action_id) + if action: + Mouse.create().click_object(action) + + @property + def cover_art(self): + return self.get_children_by_type(CoverArt) + + @property + def ratings_widget(self): + return self.get_children_by_type(PreviewRatingsWidget) + + @property + def info_hint_widget(self): + return self.get_children_by_type(PreviewInfoHintWidget) + + @property + def icon(self): + return self.get_children_by_type(IconTexture) + + @property + def text_boxes(self): + return self.get_children_by_type(StaticCairoText) + + +class ApplicationPreview(Preview): + """A application preview of a dash scope result.""" + +class GenericPreview(Preview): + """A generic preview of a dash scope result.""" + +class MusicPreview(Preview): + """A music preview of a dash scope result.""" + +class MoviePreview(Preview): + """A movie preview of a dash scope result.""" + +class PreviewContent(UnityIntrospectionObject): + """A preview content layout for the dash previews.""" + + def get_current_preview(self): + previews = self.get_children_by_type(Preview) + if len(previews) > 0: + return previews[0] + return None + + +class PreviewContainer(UnityIntrospectionObject): + """A container view for the main dash preview widget.""" + + @property + def content(self): + return self.get_content() + + def get_num_previews(self): + """Get the number of previews queued and current in the container.""" + previews = self.content.get_children_by_type(Preview) + return len(previews) + + def get_content(self): + """Get the preview content layout for the container.""" + return self.get_children_by_type(PreviewContent)[0] + + def get_left_navigator(self): + """Return the left navigator object""" + navigators = self.get_children_by_type(PreviewNavigator) + for nav in navigators: + if nav.direction == 2: + return nav + return None + + def get_right_navigator(self): + """Return the right navigator object""" + navigators = self.get_children_by_type(PreviewNavigator) + for nav in navigators: + if nav.direction == 3: + return nav + return None + + def navigate_left(self, count=1): + """Navigate preview left""" + m = Mouse.create() + m.move_to_object(self.get_left_navigator().button_geo) + + old_preview_initiate_count = self.preview_initiate_count + + for i in range(count): + self.navigate_left_enabled.wait_for(True) + m.click() + self.preview_initiate_count.wait_for(GreaterThan(old_preview_initiate_count)) + old_preview_initiate_count = self.preview_initiate_count + + def navigate_right(self, count=1): + """Navigate preview right""" + m = Mouse.create() + m.move_to_object(self.get_right_navigator().button_geo) + + old_preview_initiate_count = self.preview_initiate_count + + for i in range(count): + self.navigate_right_enabled.wait_for(True) + m.click() + self.preview_initiate_count.wait_for(GreaterThan(old_preview_initiate_count)) + old_preview_initiate_count = self.preview_initiate_count + + @property + def animating(self): + """Return True if the preview is animating, False otherwise.""" + return self.content.animating + + @property + def waiting_preview(self): + """Return True if waiting for a preview, False otherwise.""" + return self.content.waiting_preview + + @property + def animation_progress(self): + """Return the progress of the current preview animation.""" + return self.content.animation_progress + + @property + def current_preview(self): + """Return the current preview object.""" + return self.content.get_current_preview() + preview_initiate_count_ + + @property + def preview_initiate_count(self): + """Return the number of initiated previews since opened.""" + return self.content.preview_initiate_count + + @property + def navigation_complete_count(self): + """Return the number of completed previews since opened.""" + return self.content.navigation_complete_count + + @property + def relative_nav_index(self): + """Return the navigation position relative to the direction of movement.""" + return self.content.relative_nav_index + + @property + def navigate_right_enabled(self): + """Return True if right preview navigation is enabled, False otherwise.""" + return self.content.navigate_right_enabled + + @property + def navigate_left_enabled(self): + """Return True if left preview navigation is enabled, False otherwise.""" + return self.content.navigate_left_enabled + + +class PreviewNavigator(UnityIntrospectionObject): + """A view containing a button to nagivate between previews.""" + + def icon(self): + return self.get_children_by_type(IconTexture); + + +class PreviewInfoHintWidget(UnityIntrospectionObject): + """A view containing additional info for a preview.""" + + +class PreviewRatingsWidget(UnityIntrospectionObject): + """A view containing a rating button and user rating count.""" + + +class Tracks(UnityIntrospectionObject): + """A view containing music tracks.""" + + +class Track(UnityIntrospectionObject): + """A singular music track for dash prevews.""" + + +class ActionButton(UnityIntrospectionObject): + """A preview action button.""" + + +class IconTexture(UnityIntrospectionObject): + """An icon for the preview.""" + + +class StaticCairoText(UnityIntrospectionObject): + """Text boxes in the preview""" + only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/hud.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/hud.py @@ -0,0 +1,169 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +from logging import getLogger + +from autopilot.input import Keyboard +from autopilot.introspection.dbus import StateNotFoundError +from autopilot.keybindings import KeybindingsHelper +from HTMLParser import HTMLParser +import re + +from unity.emulators import UnityIntrospectionObject +from unity.emulators.dash import SearchBar +from unity.emulators.icons import HudEmbeddedIcon, HudLauncherIcon + +log = getLogger(__name__) + +class HudController(UnityIntrospectionObject, KeybindingsHelper): + """Proxy object for the Unity Hud Controller.""" + + def __init__(self, *args, **kwargs): + super(HudController, self).__init__(*args, **kwargs) + self.keyboard = Keyboard.create() + + def get_hud_view(self): + views = self.get_children_by_type(HudView) + return views[0] if views else None + + def ensure_hidden(self): + """Hides the hud if it's not already hidden.""" + if self.visible: + if self.search_string: + # need this to clear the search string, and then another to + # close the hud. + self.keyboard.press_and_release("Escape") + self.search_string.wait_for("") + self.keyboard.press_and_release("Escape") + self.visible.wait_for(False) + + def ensure_visible(self): + """Shows the hud if it's not already showing.""" + if not self.visible: + self.toggle_reveal() + self.visible.wait_for(True) + + def toggle_reveal(self, tap_delay=0.1): + """Tap the 'Alt' key to toggle the hud visibility.""" + old_state = self.visible + self.keybinding("hud/reveal", tap_delay) + self.visible.wait_for(not old_state) + + def get_embedded_icon(self): + """Returns the HUD view embedded icon or None if is not shown.""" + view = self.view + if (not view): + return None + + icons = view.get_children_by_type(HudEmbeddedIcon) + return icons[0] if icons else None + + def get_launcher_icon(self): + """Returns the HUD launcher icon""" + unity = self.get_root_instance() + icons = unity.select_many(HudLauncherIcon) + assert(len(icons) == 1) + return icons[0] + + @property + def icon(self): + if self.is_locked_launcher: + return self.get_launcher_icon() + else: + return self.get_embedded_icon() + + @property + def view(self): + """Returns the HudView.""" + return self.get_hud_view() + + @property + def searchbar(self): + """Returns the searchbar attached to the hud.""" + return self.get_hud_view().searchbar + + @property + def search_string(self): + """Returns the searchbars' search string.""" + return self.searchbar.search_string + + @property + def is_locked_launcher(self): + return self.locked_to_launcher + + @property + def monitor(self): + return self.hud_monitor + + @property + def geometry(self): + return self.globalRect + + @property + def selected_button(self): + view = self.get_hud_view() + if view: + return view.selected_button + else: + return 0 + + @property + def hud_buttons(self): + """Returns a list of current HUD buttons.""" + return self.view.hud_buttons + + @property + def selected_hud_button(self): + try: + if len(self.hud_buttons) is 0: + return 0 + [button] = filter(lambda x: x.focused, self.hud_buttons) + return button + except IndexError: + raise RuntimeError("No HUD buttons found, is hud active?") + except StateNotFoundError: + log.warning("StateNotFoundError has been raised by HudController") + return 0 + + @property + def num_buttons(self): + view = self.get_hud_view() + if view: + return view.num_buttons + else: + return 0 + + +class HudView(UnityIntrospectionObject): + """Proxy object for the hud view child of the controller.""" + + @property + def searchbar(self): + """Get the search bar attached to this hud view.""" + return self.get_children_by_type(SearchBar)[0] + + @property + def hud_buttons(self): + return self.get_children_by_type(HudButton) + + @property + def geometry(self): + return self.globalRect + + +class HudButton(UnityIntrospectionObject): + """Proxy object for the hud buttons.""" + + @property + def label_no_formatting(self): + """Returns the label text with the formatting removed.""" + htmlparser = HTMLParser() + return htmlparser.unescape(re.sub("<[^>]*>", "", self.label)) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/ibus.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/ibus.py @@ -0,0 +1,113 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# +# Autopilot Functional Test Tool +# Copyright (C) 2012-2013 Canonical +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + + +"""Functions to deal with ibus service.""" + +from __future__ import absolute_import + +from gi.repository import IBus, GLib +import os +import logging +import subprocess + + +logger = logging.getLogger(__name__) + + +def get_ibus_bus(): + """Get the ibus bus object, possibly starting the ibus daemon if it's + not already running. + + :raises: **RuntimeError** in the case of ibus-daemon being unavailable. + + """ + bus = IBus.Bus() + if bus.is_connected(): + return bus + + main_loop = GLib.MainLoop() + + timeout = 5 + GLib.timeout_add_seconds(timeout, lambda *args: main_loop.quit()) + bus.connect("connected", lambda *args: main_loop.quit()) + + os.spawnlp(os.P_NOWAIT, "ibus-daemon", "ibus-daemon", "--xim") + + main_loop.run() + + if not bus.is_connected(): + raise RuntimeError( + "Could not start ibus-daemon after %d seconds." % (timeout)) + return bus + + +def get_available_input_engines(): + """Get a list of available input engines.""" + bus = get_ibus_bus() + return [e.get_name() for e in bus.list_engines()] + + +def get_active_input_engines(): + """Get the list of input engines that have been activated.""" + bus = get_ibus_bus() + return [e.get_name() for e in bus.list_active_engines()] + + +def set_active_engines(engine_list): + """Installs the engines in *engine_list* into the list of active iBus + engines. + + The specified engines must appear in the return list from + get_available_input_engines(). + + .. note:: This function removes all other engines. + + This function returns the list of engines installed before this function + was called. The caller should pass this list to set_active_engines to + restore ibus to it's old state once the test has finished. + + :param engine_list: List of engine names + :type engine_list: List of strings + :raises: **TypeError** on invalid *engine_list* parameter. + :raises: **ValueError** when engine_list contains invalid engine name. + + """ + if type(engine_list) is not list: + raise TypeError("engine_list must be a list of valid engine names.") + available_engines = get_available_input_engines() + for engine in engine_list: + if not isinstance(engine, basestring): + raise TypeError("Engines in engine_list must all be strings.") + if engine not in available_engines: + raise ValueError( + "engine_list contains invalid engine name: '%s'", engine) + + bus = get_ibus_bus() + config = bus.get_config() + + config.set_value("general", + "preload_engine_mode", + GLib.Variant.new_int32(IBus.PreloadEngineMode.USER)) + + old_engines = get_active_input_engines() + config.set_value( + "general", "preload_engines", GLib.Variant("as", engine_list)) + + return old_engines only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/icons.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/icons.py @@ -0,0 +1,110 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +from unity.emulators import UnityIntrospectionObject +from unity.emulators.quicklist import Quicklist +from unity.emulators.tooltip import ToolTip + +class SimpleLauncherIcon(UnityIntrospectionObject): + """Holds information about a simple launcher icon. + + Do not instantiate an instance of this class yourself. Instead, use the + appropriate methods in the Launcher class instead. + + """ + + @property + def center_position(self): + """Get the center point of an icon, returns a tuple with (x, y, z).""" + return self.center + + def get_quicklist(self): + """Get the quicklist for this launcher icon. + + This may return None, if there is no quicklist associated with this + launcher icon. + + """ + matches = self.get_children_by_type(Quicklist) + return matches[0] if matches else None + + def get_tooltip(self): + """Get the tooltip for this launcher icon. + + This may return None, if there is no tooltip associated with this + launcher icon. + + """ + matches = self.get_children_by_type(ToolTip) + return matches[0] if matches else None + + def is_on_monitor(self, monitor): + """Returns True if the icon is available in the defined monitor.""" + if monitor >= 0 and monitor < len(self.monitors_visibility): + return self.monitors_visibility[monitor] + + return False + + def controls_window(self, xid): + """Returns true if the icon controls the specified xid.""" + + return self.xids.contains(xid) + + +class BFBLauncherIcon(SimpleLauncherIcon): + """Represents the BFB button in the launcher.""" + + +class ExpoLauncherIcon(SimpleLauncherIcon): + """Represents the Expo button in the launcher.""" + + +class HudLauncherIcon(SimpleLauncherIcon): + """Represents the HUD button in the launcher.""" + + +class ApplicationLauncherIcon(SimpleLauncherIcon): + """Represents a launcher icon with BAMF integration.""" + + def __repr__(self): + with self.no_automatic_refreshing(): + return self._repr_string("{0.desktop_id}".format(self)) + +class TrashLauncherIcon(SimpleLauncherIcon): + """Represents the trash launcher icon.""" + + +class DeviceLauncherIcon(SimpleLauncherIcon): + """Represents a device icon in the launcher.""" + + +class DesktopLauncherIcon(SimpleLauncherIcon): + """Represents an icon that may appear in the switcher.""" + + +class VolumeLauncherIcon(SimpleLauncherIcon): + """Represents a mounted disk icon in the launcher.""" + + +class SoftwareCenterLauncherIcon(ApplicationLauncherIcon): + """Represents a launcher icon of a Software Center app.""" + + +class HudEmbeddedIcon(UnityIntrospectionObject): + """Proxy object for the hud embedded icon child of the view.""" + + @property + def geometry(self): + return self.globalRect + + +class LauncherEntry(UnityIntrospectionObject): + """Proxy for the LauncherEntryRemote instances in Unity.""" only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/launcher.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/launcher.py @@ -0,0 +1,523 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +from autopilot.input import Mouse +from autopilot.display import Display, move_mouse_to_screen +from autopilot.keybindings import KeybindingsHelper +import logging +from testtools.matchers import NotEquals +from time import sleep + +from unity.emulators import UnityIntrospectionObject +from unity.emulators.icons import ( + ApplicationLauncherIcon, + BFBLauncherIcon, + ExpoLauncherIcon, + SimpleLauncherIcon, + TrashLauncherIcon, + ) + +from unity.emulators.compiz import get_compiz_option + +logger = logging.getLogger(__name__) + + +class IconDragType: + """Define possible positions to drag an icon onto another""" + INSIDE = 0 + OUTSIDE = 1 + BEFORE = 3 + AFTER = 4 + + +class LauncherController(UnityIntrospectionObject): + """The LauncherController class.""" + + def get_launcher_for_monitor(self, monitor_num): + """Return an instance of Launcher for the specified monitor, or None.""" + launchers = self.get_children_by_type(Launcher, monitor=monitor_num) + return launchers[0] if launchers else None + + def get_launchers(self): + """Return the available launchers, or None.""" + return self.get_children_by_type(Launcher) + + @property + def model(self): + """Return the launcher model.""" + models = self.get_children_by_type(LauncherModel) + assert(len(models) == 1) + return models[0] + + +class Launcher(UnityIntrospectionObject, KeybindingsHelper): + """An individual launcher for a monitor.""" + + def __init__(self, *args, **kwargs): + super(Launcher, self).__init__(*args, **kwargs) + + self.show_timeout = 1 + self.hide_timeout = 1 + self.in_keynav_mode = False + self.in_switcher_mode = False + + self._mouse = Mouse.create() + self._display = Display.create() + + def _perform_key_nav_binding(self, keybinding): + if not self.in_keynav_mode: + raise RuntimeError("Cannot perform key navigation when not in kaynav mode.") + self.keybinding(keybinding) + + def _perform_key_nav_exit_binding(self, keybinding): + self._perform_key_nav_binding(keybinding) + self.in_keynav_mode = False + + def _perform_switcher_binding(self, keybinding): + if not self.in_switcher_mode: + raise RuntimeError("Cannot interact with launcher switcher when not in switcher mode.") + self.keybinding(keybinding) + + def _perform_switcher_exit_binding(self, keybinding): + # If we're doing a normal activation, all we need to do is release the + # keybinding. Otherwise, perform the keybinding specified *then* release + # the switcher keybinding. + if keybinding != "launcher/switcher": + self._perform_switcher_binding(keybinding) + self.keybinding_release("launcher/switcher") + self.in_switcher_mode = False + + def _get_controller(self): + """Get the launcher controller.""" + controller = self.get_root_instance().select_single(LauncherController) + return controller + + def move_mouse_to_screen_of_current_launcher(self): + """Places the mouse on the screen of this launcher.""" + move_mouse_to_screen(self.monitor) + + def move_mouse_to_right_of_launcher(self): + """Places the mouse to the right of this launcher.""" + move_mouse_to_screen(self.monitor) + (x, y, w, h) = self.geometry + target_x = x + w + 10 + target_y = y + h / 2 + + logger.debug("Moving mouse away from launcher.") + self._mouse.move(target_x, target_y, False) + sleep(self.show_timeout) + + def move_mouse_over_launcher(self): + """Move the mouse over this launcher.""" + move_mouse_to_screen(self.monitor) + logger.debug("Moving mouse to center of launcher.") + self._mouse.move_to_object(self) + + def move_mouse_to_icon(self, icon, autoscroll_offset=0): + """Move the mouse to a specific icon.""" + (x, y, w, h) = self.geometry + found = False + + # Only try 10 times (5 secs.) before giving up. + for i in xrange(0, 10): + mouse_x = target_x = icon.center.x + mouse_y = target_y = icon.center.y + if target_y > h + y: + mouse_y = h + y - autoscroll_offset + elif target_y < 0: + mouse_y = y + autoscroll_offset + if self._mouse.x == target_x and self._mouse.y == target_y: + found = True + break + self._mouse.move(mouse_x, mouse_y) + sleep(0.5) + + if not found: + raise RuntimeError("Could not move mouse to the icon") + + def mouse_reveal_launcher(self): + """Reveal this launcher with the mouse. + + If the launcher is already visible calling this method does nothing. + """ + if self.is_showing: + return + move_mouse_to_screen(self.monitor) + (x, y, w, h) = self.geometry + + target_x = x - 300 # this is the pressure we need to reveal the launcher. + target_y = y + h / 2 + logger.debug("Revealing launcher on monitor %d with mouse.", self.monitor) + self._mouse.move(target_x, target_y, True, 5, .002) + + # Move the mouse back to the launcher for multi-monitor + self._mouse.move(x, target_y, True, 5, .002) + + def keyboard_reveal_launcher(self): + """Reveal this launcher using the keyboard.""" + move_mouse_to_screen(self.monitor) + logger.debug("Revealing launcher with keyboard.") + self.keybinding_hold("launcher/reveal") + self.is_showing.wait_for(True) + + def keyboard_unreveal_launcher(self): + """Un-reveal this launcher using the keyboard.""" + move_mouse_to_screen(self.monitor) + logger.debug("Un-revealing launcher with keyboard.") + self.keybinding_release("launcher/reveal") + # only wait if the launcher is set to autohide + if self.hidemode == 1: + self.is_showing.wait_for(False) + + def keyboard_select_icon(self, **kwargs): + """Using either keynav mode or the switcher, select an icon in the launcher. + + The desired mode (keynav or switcher) must be started already before + calling this methods or a RuntimeError will be raised. + + This method won't activate the icon, it will only select it. + + Icons are selected by passing keyword argument filters to this method. + For example: + + >>> launcher.keyboard_select_icon(tooltip_text="Calculator") + + ...will select the *first* icon that has a 'tooltip_text' attribute equal + to 'Calculator'. If an icon is missing the attribute, it is treated as + not matching. + + If no icon is found, this method will raise a ValueError. + + """ + + if not self.in_keynav_mode and not self.in_switcher_mode: + raise RuntimeError("Launcher must be in keynav or switcher mode") + + launcher_model = self.get_root_instance().select_single(LauncherModel) + all_icons = launcher_model.get_launcher_icons() + logger.debug("all_icons = %r", [i.tooltip_text for i in all_icons]) + for icon in all_icons: + # can't iterate over the model icons directly since some are hidden + # from the user. + if not icon.visible: + continue + logger.debug("Selected icon = %s", icon.tooltip_text) + matches = True + for arg,val in kwargs.iteritems(): + if not hasattr(icon, arg) or getattr(icon, arg, None) != val: + matches = False + break + if matches: + return + if self.in_keynav_mode: + self.key_nav_next() + elif self.in_switcher_mode: + self.switcher_next() + raise ValueError("No icon found that matches: %r", kwargs) + + def key_nav_start(self): + """Start keyboard navigation mode by pressing Alt+F1.""" + move_mouse_to_screen(self.monitor) + logger.debug("Initiating launcher keyboard navigation with Alt+F1.") + self.keybinding("launcher/keynav") + self._get_controller().key_nav_is_active.wait_for(True) + self.in_keynav_mode = True + + def key_nav_cancel(self): + """End the key navigation.""" + logger.debug("Cancelling keyboard navigation mode.") + self._perform_key_nav_exit_binding("launcher/keynav/exit") + self._get_controller().key_nav_is_active.wait_for(False) + + def key_nav_activate(self): + """Activates the selected launcher icon. In the current implementation + this also exits key navigation""" + logger.debug("Ending keyboard navigation mode, activating icon.") + self._perform_key_nav_exit_binding("launcher/keynav/activate") + self._get_controller().key_nav_is_active.wait_for(False) + + def key_nav_next(self): + """Moves the launcher keynav focus to the next launcher icon""" + logger.debug("Selecting next item in keyboard navigation mode.") + old_selection = self._get_controller().key_nav_selection + self._perform_key_nav_binding("launcher/keynav/next") + self._get_controller().key_nav_selection.wait_for(NotEquals(old_selection)) + + def key_nav_prev(self): + """Moves the launcher keynav focus to the previous launcher icon""" + logger.debug("Selecting previous item in keyboard navigation mode.") + old_selection = self._get_controller().key_nav_selection + self._perform_key_nav_binding("launcher/keynav/prev") + self._get_controller().key_nav_selection.wait_for(NotEquals(old_selection)) + + def key_nav_enter_quicklist(self): + logger.debug("Opening quicklist for currently selected icon.") + self._perform_key_nav_binding("launcher/keynav/open-quicklist") + self.quicklist_open.wait_for(True) + + def key_nav_exit_quicklist(self): + logger.debug("Closing quicklist for currently selected icon.") + self._perform_key_nav_binding("launcher/keynav/close-quicklist") + self.quicklist_open.wait_for(False) + + def switcher_start(self): + """Start the super+Tab switcher on this launcher.""" + move_mouse_to_screen(self.monitor) + logger.debug("Starting Super+Tab switcher.") + self.keybinding_hold_part_then_tap("launcher/switcher") + self._get_controller().key_nav_is_active.wait_for(True) + self.in_switcher_mode = True + + def switcher_cancel(self): + """End the super+tab swithcer.""" + logger.debug("Cancelling keyboard navigation mode.") + self._perform_switcher_exit_binding("launcher/switcher/exit") + self._get_controller().key_nav_is_active.wait_for(False) + + def switcher_activate(self): + """Activates the selected launcher icon. In the current implementation + this also exits the switcher""" + logger.debug("Ending keyboard navigation mode.") + self._perform_switcher_exit_binding("launcher/switcher") + self._get_controller().key_nav_is_active.wait_for(False) + + def switcher_next(self): + logger.debug("Selecting next item in keyboard navigation mode.") + old_selection = self._get_controller().key_nav_selection + self._perform_switcher_binding("launcher/switcher/next") + self._get_controller().key_nav_selection.wait_for(NotEquals(old_selection)) + + def switcher_prev(self): + logger.debug("Selecting previous item in keyboard navigation mode.") + old_selection = self._get_controller().key_nav_selection + self._perform_switcher_binding("launcher/switcher/prev") + self._get_controller().key_nav_selection.wait_for(NotEquals(old_selection)) + + def switcher_up(self): + logger.debug("Selecting next item in keyboard navigation mode.") + old_selection = self._get_controller().key_nav_selection + self._perform_switcher_binding("launcher/switcher/up") + self._get_controller().key_nav_selection.wait_for(NotEquals(old_selection)) + + def switcher_down(self): + logger.debug("Selecting previous item in keyboard navigation mode.") + old_selection = self._get_controller().key_nav_selection + self._perform_switcher_binding("launcher/switcher/down") + self._get_controller().key_nav_selection.wait_for(NotEquals(old_selection)) + + def click_launcher_icon(self, icon, button=1, move_mouse_after=True): + """Move the mouse over the launcher icon, and click it. + `icon` must be an instance of SimpleLauncherIcon or it's descendants. + `move_mouse_after` moves the mouse outside the launcher if true. + """ + if not isinstance(icon, SimpleLauncherIcon): + raise TypeError("icon must be a LauncherIcon, not %s" % type(icon)) + + logger.debug("Clicking launcher icon %r on monitor %d with mouse button %d", + icon, self.monitor, button) + + self.mouse_reveal_launcher() + self.move_mouse_to_icon(icon) + self._mouse.click(button) + + if (move_mouse_after): + self.move_mouse_to_right_of_launcher() + + def drag_icon_to_position(self, icon, pos, target, drag_type=IconDragType.INSIDE): + """Drag a launcher icon to a new position. + + 'icon' is the icon to move. It must be either a ApplicationLauncherIcon or an + ExpoLauncherIcon instance. Other values will result in a TypeError being + raised. + + 'pos' must be one of IconDragType.BEFORE or IconDragType.AFTER. If it is + not one of these, a ValueError will be raised. + + 'target' is the target icon. + + 'drag_type' must be one of IconDragType.INSIDE or IconDragType.OUTSIDE. + This specifies whether the icon is gragged inside the launcher, or to the + right of it. The default is to drag inside the launcher. If it is + specified, and not one of the allowed values, a ValueError will be raised. + + For example: + + >>> drag_icon_to_position(calc_icon, IconDragType.BEFORE, switcher_icon) + + This will drag the calculator icon to just before the switcher icon. + + Note: This method makes no attempt to sanity-check the requested move. + For example, it will happily try and move an icon to before the BFB icon, + if asked. + + """ + if not isinstance(icon, ApplicationLauncherIcon) \ + and not isinstance(icon, ExpoLauncherIcon): + raise TypeError("Icon to move must be a ApplicationLauncherIcon or ExpoLauncherIcon, not %s" + % type(icon).__name__) + + if pos not in (IconDragType.BEFORE, IconDragType.AFTER): + raise ValueError("'pos' parameter must be one of IconDragType.BEFORE, IconDragType.AFTER") + + if not isinstance(target, SimpleLauncherIcon): + raise TypeError("'target' must be a valid launcher icon, not %s" + % type(target).__name__) + + if drag_type not in (IconDragType.INSIDE, IconDragType.OUTSIDE): + raise ValueError("'drag_type' parameter must be one of IconDragType.INSIDE, IconDragType.OUTSIDE") + + icon_height = get_compiz_option("unityshell", "icon_size") + + self.move_mouse_to_icon(icon) + self._mouse.press() + sleep(1) + + if drag_type == IconDragType.OUTSIDE: + shift_over = self._mouse.x + (icon_height * 2) + self._mouse.move(shift_over, self._mouse.y, rate=20, time_between_events=0.005) + sleep(0.5) + + self.move_mouse_to_icon(target) + + target_y = target.center.y + if target_y < icon.center.y: + target_y += icon_height + if pos == IconDragType.BEFORE: + target_y -= icon_height + (icon_height / 2) + + self._mouse.move(self._mouse.x, target_y, rate=20, time_between_events=0.005) + sleep(1) + self._mouse.release() + self.move_mouse_to_right_of_launcher() + + def lock_to_launcher(self, icon): + """lock 'icon' to the launcher, if it's not already. + `icon` must be an instance of ApplicationLauncherIcon. + """ + if not isinstance(icon, ApplicationLauncherIcon): + raise TypeError("Can only lock instances of ApplicationLauncherIcon") + if icon.sticky: + # Nothing to do. + return + + logger.debug("Locking icon %r to launcher.", icon) + self.click_launcher_icon(icon, button=3) + quicklist = icon.get_quicklist() + pin_item = quicklist.get_quicklist_item_by_text('Lock to Launcher') + quicklist.click_item(pin_item) + + def unlock_from_launcher(self, icon): + """lock 'icon' to the launcher, if it's not already. + + `icon` must be an instance of ApplicationLauncherIcon. + + """ + if not isinstance(icon, ApplicationLauncherIcon): + raise TypeError("Can only unlock instances of ApplicationLauncherIcon, not %s" % type(icon).__name__) + if not icon.sticky: + # nothing to do. + return + + logger.debug("Unlocking icon %r from launcher.") + self.click_launcher_icon(icon, button=3) + quicklist = icon.get_quicklist() + pin_item = quicklist.get_quicklist_item_by_text('Unlock from Launcher') + quicklist.click_item(pin_item) + + @property + def geometry(self): + """Returns a Rectangle (x,y,w,h) for the current launcher.""" + return self.globalRect + + +class LauncherModel(UnityIntrospectionObject): + """The launcher model. Contains all launcher icons as children.""" + + def get_bfb_icon(self): + icons = self.get_root_instance().select_many(BFBLauncherIcon) + assert(len(icons) == 1) + return icons[0] + + def get_expo_icon(self): + icons = self.get_children_by_type(ExpoLauncherIcon) + assert(len(icons) == 1) + return icons[0] + + def get_trash_icon(self): + icons = self.get_children_by_type(TrashLauncherIcon) + assert(len(icons) == 1) + return icons[0] + + def get_launcher_icons(self, visible_only=True): + """Get a list of launcher icons in this launcher.""" + if visible_only: + icons = self.get_children_by_type(SimpleLauncherIcon, visible=True) + else: + icons = self.get_children_by_type(SimpleLauncherIcon) + return sorted(icons, key=lambda icon: icon.order) + + def get_bamf_launcher_icons(self, visible_only=True): + """Get a list of bamf launcher icons in this launcher.""" + if visible_only: + return self.get_children_by_type(ApplicationLauncherIcon, visible=True) + else: + return self.get_children_by_type(ApplicationLauncherIcon) + + def get_launcher_icons_for_monitor(self, monitor, visible_only=True): + """Get a list of launcher icons for provided monitor.""" + icons = [] + for icon in self.get_launcher_icons(visible_only): + if icon.is_on_monitor(monitor): + icons.append(icon) + + return icons + + def get_icon(self, **kwargs): + """Get a launcher icon from the model according to some filters. + + This method accepts keyword argument that are the filters to use when + looking for an icon. For example, to find an icon with a particular + desktop_id, one might do this from within a test: + + >>> self.launcher.model.get_icon(desktop_id="gcalctool.desktop") + + This method returns only one icon. It is the callers responsibility to + ensure that the filter matches only one icon. + + This method will attempt to get the launcher icon, and will retry several + times, so the caller can be assured that if this method doesn't find + the icon it really does not exist. + + If no keyword arguments are specified, ValueError will be raised. + + If no icons are matched, None is returned. + + """ + + if not kwargs: + raise ValueError("You must specify at least one keyword argument to ths method.") + + for i in range(10): + icons = self.get_children_by_type(SimpleLauncherIcon, **kwargs) + if len(icons) > 1: + logger.warning("Got more than one icon returned using filters=%r. Returning first one", kwargs) + if icons: + return icons[0] + sleep(1) + return None + + def num_launcher_icons(self): + """Get the number of icons in the launcher model.""" + return len(self.get_launcher_icons()) + + def num_bamf_launcher_icons(self, visible_only=True): + """Get the number of bamf icons in the launcher model.""" + return len(self.get_bamf_launcher_icons(visible_only)) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/panel.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/panel.py @@ -0,0 +1,346 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +import logging +from time import sleep + +from autopilot.input import Mouse +from autopilot.keybindings import KeybindingsHelper +from autopilot.introspection.types import Rectangle + +from unity.emulators import UnityIntrospectionObject +logger = logging.getLogger(__name__) + + +class PanelController(UnityIntrospectionObject): + """The PanelController class.""" + + def get_panel_for_monitor(self, monitor_num): + """Return an instance of panel for the specified monitor, or None.""" + panels = self.get_children_by_type(UnityPanel, monitor=monitor_num) + assert(len(panels) == 1) + return panels[0] + + def get_active_panel(self): + """Return the active panel, or None.""" + panels = self.get_children_by_type(UnityPanel, active=True) + assert(len(panels) == 1) + return panels[0] + + def get_active_indicator(self): + for panel in self.get_panels: + active = panel.get_active_indicator() + if active: + return active + + return None + + @property + def get_panels(self): + """Return the available panels, or None.""" + return self.get_children_by_type(UnityPanel) + + +class UnityPanel(UnityIntrospectionObject, KeybindingsHelper): + """An individual panel for a monitor.""" + + def __init__(self, *args, **kwargs): + super(UnityPanel, self).__init__(*args, **kwargs) + self._mouse = Mouse.create() + + def __get_menu_view(self): + """Return the menu view.""" + menus = self.get_children_by_type(MenuView) + assert(len(menus) == 1) + return menus[0] + + def __get_window_buttons(self): + """Return the window buttons view.""" + buttons = self.menus.get_children_by_type(WindowButtons) + assert(len(buttons) == 1) + return buttons[0] + + def __get_grab_area(self): + """Return the panel grab area.""" + grab_areas = self.menus.get_children_by_type(GrabArea) + assert(len(grab_areas) == 1) + return grab_areas[0] + + def __get_indicators_view(self): + """Return the menu view.""" + indicators = self.get_children_by_type(Indicators) + assert(len(indicators) == 1) + return indicators[0] + + def move_mouse_below_the_panel(self): + """Places the mouse to bottom of this panel.""" + (x, y, w, h) = self.geometry + target_x = x + w / 2 + target_y = y + h + 10 + + logger.debug("Moving mouse away from panel.") + self._mouse.move(target_x, target_y) + + def move_mouse_over_menus(self): + """Move the mouse over the menu area for this panel.""" + (x, y, w, h) = self.menus.geometry + target_x = x + w / 2 + target_y = y + h / 2 + + # The menu view has bigger geometry than the real layout + menu_entries = self.menus.get_entries() + if len(menu_entries) > 0: + first_x = menu_entries[0].x + last_x = menu_entries[-1].x + menu_entries[-1].width / 2 + + target_x = first_x + (last_x - first_x) / 2 + + logger.debug("Moving mouse to center of menu area.") + self._mouse.move(target_x, target_y) + + def move_mouse_over_grab_area(self): + """Move the mouse over the grab area for this panel.""" + logger.debug("Moving mouse to center of grab area.") + self._mouse.move_to_object(self.grab_area) + + def move_mouse_over_window_buttons(self): + """Move the mouse over the center of the window buttons area for this panel.""" + logger.debug("Moving mouse to center of the window buttons.") + self._mouse.move_to_object(self.window_buttons) + + def move_mouse_over_indicators(self): + """Move the mouse over the center of the indicators area for this panel.""" + logger.debug("Moving mouse to center of the indicators area.") + self._mouse.move_to_object(self.indicators) + + def get_indicator_entries(self, visible_only=True, include_hidden_menus=False): + """Returns a list of entries for this panel including both menus and indicators""" + entries = [] + if include_hidden_menus or self.menus_shown: + entries = self.menus.get_entries() + entries += self.indicators.get_ordered_entries(visible_only) + return entries + + def get_active_indicator(self): + """Returns the indicator entry that is currently active""" + entries = self.get_indicator_entries(False, True) + entries = filter(lambda e: e.active == True, entries) + assert(len(entries) <= 1) + return entries[0] if entries else None + + def get_indicator_entry(self, entry_id): + """Returns the indicator entry for the given ID or None""" + entries = self.get_indicator_entries(False, True) + entries = filter(lambda e: e.entry_id == entry_id, entries) + assert(len(entries) <= 1) + return entries[0] if entries else None + + @property + def title(self): + return self.menus.panel_title + + @property + def focused(self): + return self.menus.focused + + @property + def desktop_is_active(self): + return self.menus.desktop_active + + @property + def menus_shown(self): + return self.active and self.menus.draw_menus + + @property + def window_buttons_shown(self): + return self.menus.draw_window_buttons + + @property + def window_buttons(self): + return self.__get_window_buttons() + + @property + def menus(self): + return self.__get_menu_view() + + @property + def grab_area(self): + return self.__get_grab_area() + + @property + def indicators(self): + return self.__get_indicators_view() + + @property + def geometry(self): + """Returns a Rectangle (x,y,w,h) for the current panel.""" + return self.globalRect + + +class MenuView(UnityIntrospectionObject): + """The Menu View class.""" + + def get_entries(self): + """Return a list of menu entries""" + entries = self.get_children_by_type(IndicatorEntry) + # We need to filter out empty entries, which are seperators - those + # are not valid, visible and working entries + # For instance, gedit adds some of those, breaking our tests + entries = [e for e in entries if (e.label != "")] + return entries + + def get_menu_by_label(self, entry_label): + """Return the first indicator entry found with the given label""" + indicators = self.get_children_by_type(IndicatorEntry, label=entry_label) + return indicators[0] if indicators else None + + @property + def geometry(self): + """Returns a Rectangle (x,y,w,h) for the current menu view.""" + return self.globalRect + + +class WindowButtons(UnityIntrospectionObject): + """The window buttons class""" + + def get_buttons(self, visible_only=True): + """Return a list of window buttons""" + if visible_only: + return self.get_children_by_type(WindowButton, visible=True) + else: + return self.get_children_by_type(WindowButton) + + def get_button(self, type): + buttons = self.get_children_by_type(WindowButton, type=type) + assert(len(buttons) == 1) + return buttons[0] + + @property + def visible(self): + return len(self.get_buttons()) != 0 + + @property + def close(self): + return self.get_button("Close") + + @property + def minimize(self): + return self.get_button("Minimize") + + @property + def unmaximize(self): + return self.get_button("Unmaximize") + + @property + def maximize(self): + return self.get_button("Maximize") + + @property + def geometry(self): + """Returns a Rectangle (x,y,w,h) for the current panel.""" + return self.globalRect + + +class WindowButton(UnityIntrospectionObject): + """The Window WindowButton class.""" + + def __init__(self, *args, **kwargs): + super(WindowButton, self).__init__(*args, **kwargs) + self._mouse = Mouse.create() + + def mouse_move_to(self): + self._mouse.move_to_object(self) + + def mouse_click(self): + # Ignore buttons that are placed at 0x0, as they're invisible yet + if not self.x and not self.y and not self.visible: + return + + self._mouse.click_object(self) + sleep(.01) + + @property + def geometry(self): + """Returns a Rectangle (x,y,w,h) for the window button.""" + return self.globalRect + + def __repr__(self): + with self.no_automatic_refreshing(): + details = "type={0.type} state={0.visual_state} sensitive={0.sensitive}".format(self) + return self._repr_string(details) + + +class GrabArea(UnityIntrospectionObject): + """The grab area class""" + + @property + def geometry(self): + """Returns a Rectangle (x,y,w,h) for the grab area.""" + return self.globalRect + + +class Indicators(UnityIntrospectionObject): + """The Indicators View class.""" + + def get_ordered_entries(self, visible_only=True): + """Return a list of indicators, ordered by their priority""" + + if visible_only: + entries = self.get_children_by_type(IndicatorEntry, visible=True) + else: + entries = self.get_children_by_type(IndicatorEntry) + + return sorted(entries, key=lambda entry: entry.priority) + + def get_indicator_by_name_hint(self, name_hint): + """Return the IndicatorEntry with the name_hint""" + indicators = self.get_children_by_type(IndicatorEntry, name_hint=name_hint) + assert(len(indicators) == 1) + return indicators[0] + + @property + def geometry(self): + """Returns a Rectangle (x,y,w,h) for the indicators area.""" + return self.globalRect + + +class IndicatorEntry(UnityIntrospectionObject): + """The IndicatorEntry View class.""" + + def __init__(self, *args, **kwargs): + super(IndicatorEntry, self).__init__(*args, **kwargs) + self._mouse = Mouse.create() + + def mouse_move_to(self): + self._mouse.move_to_object(self) + + def mouse_click(self, button=1): + self._mouse.click_object(self, button=button) + sleep(.01) + + @property + def geometry(self): + """Returns a Rectangle (x,y,w,h) for the indicator entry.""" + return self.globalRect + + @property + def menu_geometry(self): + """Returns a Rectangle (x,y,w,h) for the opened menu geometry.""" + return Rectangle(self.menu_x, self.menu_y, self.menu_width, self.menu_height) + + def __repr__(self): + with self.no_automatic_refreshing(): + details = "label={0.label}".format(self) + return self._repr_string(details) + + +class Tray(UnityIntrospectionObject): + """A panel tray object.""" only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/quicklist.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/quicklist.py @@ -0,0 +1,100 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +import logging + +from autopilot.input import Mouse + +from unity.emulators import UnityIntrospectionObject + +logger = logging.getLogger(__name__) + + +class Quicklist(UnityIntrospectionObject): + """Represents a quicklist.""" + + @property + def items(self): + """Individual items in the quicklist.""" + return self.get_children_by_type(QuicklistMenuItem, visible=True) + + @property + def selectable_items(self): + """Items that can be selected in the quicklist.""" + return self.get_children_by_type(QuicklistMenuItem, visible=True, selectable=True) + + def get_quicklist_item_by_text(self, text): + """Returns a QuicklistMenuItemLabel object with the given text, or None.""" + if not self.active: + raise RuntimeError("Cannot get quicklist items. Quicklist is inactive!") + + matches = self.get_children_by_type(QuicklistMenuItemLabel, text=text) + + return matches[0] if matches else None + + def get_quicklist_application_item(self, application_name): + """Returns the QuicklistMenuItemLabel for the given application_name""" + return self.get_quicklist_item_by_text(""+application_name+"") + + def click_item(self, item): + """Click one of the quicklist items.""" + if not isinstance(item, QuicklistMenuItem): + raise TypeError("Item must be a subclass of QuicklistMenuItem") + + item.mouse_click() + + def move_mouse_to_right(self): + """Moves the mouse outside the quicklist""" + logger.debug("Moving mouse outside the quicklist %r", self) + target_x = self.x + self.width + 10 + target_y = self.y + self.height / 2 + Mouse.create().move(target_x, target_y, animate=False) + + @property + def selected_item(self): + items = self.get_children_by_type(QuicklistMenuItem, selected=True) + assert(len(items) <= 1) + return items[0] if items else None + + @property + def geometry(self): + """Returns a Rectangle (x,y,w,h) for the quicklist.""" + return self.globalRect + + +class QuicklistMenuItem(UnityIntrospectionObject): + """Represents a single item in a quicklist.""" + + def __init__(self, *args, **kwargs): + super(QuicklistMenuItem, self).__init__(*args, **kwargs) + self._mouse = Mouse.create() + + @property + def geometry(self): + """Returns a Rectangle (x,y,w,h) for the quicklist item.""" + return self.globalRect + + def mouse_move_to(self): + assert(self.visible) + logger.debug("Moving mouse over quicklist item %r", self) + self._mouse.move_to_object(self) + + def mouse_click(self, button=1): + logger.debug("Clicking on quicklist item %r", self) + self._mouse.click_object(self) + + +class QuicklistMenuItemLabel(QuicklistMenuItem): + """Represents a text label inside a quicklist.""" + + +class QuicklistMenuItemSeparator(QuicklistMenuItem): + """Represents a separator in a quicklist.""" only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/screen.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/screen.py @@ -0,0 +1,76 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +import logging +from unity.emulators import UnityIntrospectionObject +from testtools.matchers import GreaterThan + +from autopilot.introspection.types import Rectangle +from unity.emulators.dash import SearchBar + +logger = logging.getLogger(__name__) + + +class Screen(UnityIntrospectionObject): + """The Screen class.""" + + @property + def windows(self): + """Return the available windows, or None.""" + return self.get_children_by_type(Window) + + @property + def scaled_windows(self): + """Return the available scaled windows, or None.""" + return self.get_children_by_type(Window, scaled=True) + + @property + def spread_filter(self): + """Return the spread filter, or None.""" + filter = self.get_children_by_type(SpreadFilter) + if len(filter): + return filter[0] + + return None + + def window(self, xid): + """Return the window with given xid.""" + windows = self.get_children_by_type(Window, xid=xid) + if len(windows): + return windows[0] + + return None + + +class Window(UnityIntrospectionObject): + """An individual window.""" + + @property + def geometry(self): + """Returns a Rectangle (x,y,w,h) for the current window.""" + return self.globalRect + + @property + def scale_close_geometry(self): + """Returns a Rectangle (x,y,w,h) for the scale close button.""" + self.scaled_close_width.wait_for(GreaterThan(0)) + self.scaled_close_height.wait_for(GreaterThan(0)) + return Rectangle(self.scaled_close_x, self.scaled_close_y, self.scaled_close_width, self.scaled_close_height) + + +class SpreadFilter(UnityIntrospectionObject): + """The spread filter.""" + + @property + def search_bar(self): + """Return the search bar.""" + [search_bar] = self.get_children_by_type(SearchBar) + return search_bar only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/shortcut_hint.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/shortcut_hint.py @@ -0,0 +1,59 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +import logging + +from autopilot.keybindings import KeybindingsHelper + +from unity.emulators import UnityIntrospectionObject + +logger = logging.getLogger(__name__) + +class ShortcutView(UnityIntrospectionObject): + """Proxy object for the shortcut view child of the controller.""" + +class ShortcutController(UnityIntrospectionObject, KeybindingsHelper): + """ShortcutController proxy class.""" + + def get_shortcut_view(self): + views = self.get_children_by_type(ShortcutView) + return views[0] if views else None + + def show(self): + """Push the keys necessary to reveal the shortcut hint, but don't block.""" + logger.debug("Revealing shortcut hint with keyboard.") + self.keybinding_hold("shortcuthint/reveal") + + def ensure_visible(self): + """Block until the shortcut hint is visible.""" + if not self.visible: + logger.debug("Revealing shortcut hint with keyboard.") + self.keybinding_hold("shortcuthint/reveal") + self.visible.wait_for(True) + + def hide(self): + """Release the keys keeping the shortcut hint open, but don't block.""" + logger.debug("Un-revealing shortcut hint with keyboard.") + self.keybinding_release("shortcuthint/reveal") + + def ensure_hidden(self): + """Block until the shortcut hint is hidden.""" + if self.visible: + logger.debug("Un-revealing shortcut hint with keyboard.") + self.keybinding_release("shortcuthint/reveal") + self.visible.wait_for(False) + + def cancel(self): + logger.debug("Hide the shortcut hint with keyboard, without releasing the reveal key.") + self.keybinding("shortcuthint/cancel") + + def get_show_timeout(self): + return self.timeout_duration / 1000.0 only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/switcher.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/switcher.py @@ -0,0 +1,295 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +import logging + +from autopilot.input import Mouse +from autopilot.keybindings import KeybindingsHelper + +from unity.emulators import UnityIntrospectionObject +# even though we don't use these directly, we need to make sure they've been +# imported so the classes contained are registered with the introspection API. +from unity.emulators.icons import * + + +logger = logging.getLogger(__name__) + +class SwitcherMode(): + """Define the possible modes the switcher can be in""" + NORMAL = 0 + ALL = 1 + DETAIL = 2 + + +class SwitcherDirection(): + """Directions the switcher can switch in.""" + FORWARDS = 0 + BACKWARDS = 1 + + +class SwitcherController(UnityIntrospectionObject, KeybindingsHelper): + """A class for interacting with the switcher. + + Abstracts out switcher implementation, and makes the necessary functionality available + to consumer code. + + """ + + def __init__(self, *args, **kwargs): + super(SwitcherController, self).__init__(*args, **kwargs) + self._mouse = Mouse.create() + + def get_switcher_view(self): + views = self.get_children_by_type(SwitcherView) + return views[0] if views else None + + @property + def view(self): + """Returns the SwitcherView.""" + return self.get_switcher_view() + + @property + def model(self): + models = self.get_children_by_type(SwitcherModel) + return models[0] if models else None + + @property + def icons(self): + """The set of icons in the switcher model + + """ + return self.model.icons + + @property + def current_icon(self): + """The currently selected switcher icon""" + return self.icons[self.selection_index] + + @property + def selection_index(self): + """The index of the currently selected icon""" + return self.model.selection_index + + @property + def label(self): + """The current switcher label""" + return self.view.label + + @property + def label_visible(self): + """The switcher label visibility""" + return self.view.label_visible + + @property + def mode(self): + """Returns the SwitcherMode that the switcher is currently in.""" + if not self.visible: + return None + if self.model.detail_selection and not self.model.only_apps_on_viewport: + return SwitcherMode.DETAIL, SwitcherMode.ALL + elif self.model.detail_selection: + return SwitcherMode.DETAIL + elif not self.model.only_apps_on_viewport: + return SwitcherMode.ALL + else: + return SwitcherMode.NORMAL + + def initiate(self, mode=SwitcherMode.NORMAL): + """Initiates the switcher in designated mode. Defaults to NORMAL""" + if mode == SwitcherMode.NORMAL: + logger.debug("Initiating switcher with Alt+Tab") + self.keybinding_hold_part_then_tap("switcher/reveal_normal") + self.visible.wait_for(True) + elif mode == SwitcherMode.DETAIL: + logger.debug("Initiating switcher detail mode with Alt+`") + self.keybinding_hold_part_then_tap("switcher/reveal_details") + self.visible.wait_for(True) + elif mode == SwitcherMode.ALL: + logger.debug("Initiating switcher in 'all workspaces' mode. Ctrl+Alt+Tab") + self.keybinding_hold_part_then_tap("switcher/reveal_all") + self.model.only_apps_on_viewport.wait_for(False) + + def next_icon(self): + """Move to the next icon.""" + logger.debug("Selecting next item in switcher.") + self.keybinding("switcher/next") + + def previous_icon(self): + """Move to the previous icon.""" + logger.debug("Selecting previous item in switcher.") + self.keybinding("switcher/prev") + + def select_icon(self, direction, **kwargs): + """Select an icon in the switcher. + + direction must be one of SwitcherDirection.FORWARDS or SwitcherDirection.BACKWARDS. + + The keyword arguments are used to select an icon. For example, you might + do this to select the 'Show Desktop' icon: + + >>> self.switcher.select_icon(SwitcherDirection.BACKWARDS, tooltip_text="Show Desktop") + + The switcher must be initiated already, and must be in normal mode when + this method is called, or a RuntimeError will be raised. + + If no icon matches, a ValueError will be raised. + + """ + if self.mode == SwitcherMode.DETAIL: + raise RuntimeError("Switcher must be initiated in normal mode before calling this method.") + + if direction not in (SwitcherDirection.BACKWARDS, SwitcherDirection.FORWARDS): + raise ValueError("direction must be one of SwitcherDirection.BACKWARDS, SwitcherDirection.FORWARDS") + + for i in self.model.icons: + current_icon = self.current_icon + passed=True + for key,val in kwargs.iteritems(): + if not hasattr(current_icon, key) or getattr(current_icon, key) != val: + passed=False + if passed: + return + if direction == SwitcherDirection.FORWARDS: + self.next_icon() + elif direction == SwitcherDirection.BACKWARDS: + self.previous_icon() + raise ValueError("No icon found in switcher model that matches: %r" % kwargs) + + def cancel(self): + """Stop switcher without activating the selected icon and releasing the keys. + + NOTE: Does not release Alt. + + """ + logger.debug("Cancelling switcher.") + self.keybinding("switcher/cancel") + self.visible.wait_for(False) + + def terminate(self): + """Stop switcher without activating the selected icon.""" + logger.debug("Terminating switcher.") + self.keybinding("switcher/cancel") + self.keybinding_release("switcher/reveal_normal") + self.visible.wait_for(False) + + def select(self): + """Stop switcher and activate the selected icon.""" + logger.debug("Stopping switcher") + self.keybinding_release("switcher/reveal_normal") + self.visible.wait_for(False) + + def next_via_mouse(self): + """Move to the next icon using the mouse scroll wheel.""" + logger.debug("Selecting next item in switcher with mouse scroll wheel.") + self._mouse.press(7) + self._mouse.release(7) + + def previous_via_mouse(self): + """Move to the previous icon using the mouse scroll wheel.""" + logger.debug("Selecting previous item in switcher with mouse scroll wheel.") + self._mouse.press(6) + self._mouse.release(6) + + @property + def detail_selection_index(self): + """The index of the currently selected detail""" + return self.model.detail_selection_index + + @property + def detail_current_count(self): + """The number of shown details""" + return self.model.detail_current_count + + def show_details(self): + """Show detail mode.""" + logger.debug("Showing details view.") + self.keybinding("switcher/detail_start") + self.model.detail_selection.wait_for(True) + + def hide_details(self): + """Hide detail mode.""" + logger.debug("Hiding details view.") + self.keybinding("switcher/detail_stop") + self.model.detail_selection.wait_for(False) + + def next_detail(self): + """Move to next detail in the switcher.""" + logger.debug("Selecting next item in details mode.") + self.keybinding("switcher/detail_next") + + def previous_detail(self): + """Move to the previous detail in the switcher.""" + logger.debug("Selecting previous item in details mode.") + self.keybinding("switcher/detail_prev") + + +class SwitcherView(UnityIntrospectionObject): + """An emulator class for interacting with with SwitcherView.""" + + def __init__(self, *args, **kwargs): + super(SwitcherView, self).__init__(*args, **kwargs) + self._mouse = Mouse.create() + + @property + def icon_args(self): + return self.get_children_by_type(RenderArgs); + + @property + def detail_icons(self): + return self.get_children_by_type(LayoutWindow); + + def move_over_icon(self, index): + offset = self.spread_offset + icon_arg = self.icon_args[index] + + x = icon_arg.logical_center.x + offset + y = icon_arg.logical_center.y + offset + + self._mouse.move(x,y) + + def move_over_detail_icon(self, index): + offset = self.spread_offset + x = self.detail_icons[index].x + offset + y = self.detail_icons[index].y + offset + + self._mouse.move(x,y) + + def break_mouse_bump_detection(self): + """ Only break mouse detection if the switcher is open. + Move the mouse back to the orginal position + """ + + old_x = self._mouse.x + old_y = self._mouse.y + + self.move_over_icon(0) + + x = self._mouse.x + y = self._mouse.y + self._mouse.move(x + 5, y + 5) + self._mouse.move(x - 5, y - 5) + + self._mouse.move(old_x, old_y) + + +class RenderArgs(UnityIntrospectionObject): + """An emulator class for interacting with the RenderArgs class.""" + +class LayoutWindow(UnityIntrospectionObject): + """An emulator class for interacting with the LayoutWindows class.""" + +class SwitcherModel(UnityIntrospectionObject): + """An emulator class for interacting with the SwitcherModel.""" + + @property + def icons(self): + icons = self.get_children_by_type(SimpleLauncherIcon) + return sorted(icons, key=lambda icon: icon.order) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/tooltip.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/tooltip.py @@ -0,0 +1,16 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Andrea Azzarone +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +from unity.emulators import UnityIntrospectionObject + + +class ToolTip(UnityIntrospectionObject): + """Represents a tooltip.""" only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/unity.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/unity.py @@ -0,0 +1,106 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +from dbus import Interface, SessionBus + +from unity.emulators import UnityIntrospectionObject +from unity.emulators.dash import DashController +from unity.emulators.hud import HudController +from unity.emulators.launcher import LauncherController +from unity.emulators.panel import PanelController +from unity.emulators.screen import Screen +from unity.emulators.shortcut_hint import ShortcutController +from unity.emulators.switcher import SwitcherController +from unity.emulators.window_manager import WindowManager + + +# acquire the debugging dbus object +UNITY_BUS_NAME = 'com.canonical.Unity' +DEBUG_PATH = '/com/canonical/Unity/Debug' +LOGGING_IFACE = 'com.canonical.Unity.Debug.Logging' + + +def get_dbus_proxy_object(): + return SessionBus().get_object(UNITY_BUS_NAME, DEBUG_PATH) + + +def get_dbus_logging_interface(): + return Interface(get_dbus_proxy_object(), LOGGING_IFACE) + + +def start_log_to_file(file_path): + """Instruct Unity to start logging to the given file.""" + get_dbus_logging_interface().StartLogToFile(file_path) + + +def reset_logging(): + """Instruct Unity to stop logging to a file.""" + get_dbus_logging_interface().ResetLogging() + + +def set_log_severity(component, severity): + """Instruct Unity to set a log component's severity. + + 'component' is the unity logging component name. + + 'severity' is the severity name (like 'DEBUG', 'INFO' etc.) + + """ + get_dbus_logging_interface().SetLogSeverity(component, severity) + + +def log_unity_message(severity, message): + """Instruct unity to log a message for us. + + severity: one of ('TRACE', 'DEBUG', 'INFO', 'WARNING', 'ERROR'). + + message: The message to log. + + For debugging purposes only! If you want to log a message during an autopilot + test, use the python logging framework instead. + + """ + get_dbus_logging_interface().LogMessage(severity, message) + + +class Unity(UnityIntrospectionObject): + + @property + def screen(self): + return self.get_children_by_type(Screen)[0] + + @property + def dash(self): + return self.get_children_by_type(DashController)[0] + + @property + def hud(self): + return self.get_children_by_type(HudController)[0] + + @property + def launcher(self): + return self.get_children_by_type(LauncherController)[0] + + @property + def panels(self): + return self.get_children_by_type(PanelController)[0] + + @property + def switcher(self): + return self.get_children_by_type(SwitcherController)[0] + + @property + def shortcut_hint(self): + return self.get_children_by_type(ShortcutController)[0] + + @property + def window_manager(self): + return self.get_children_by_type(WindowManager)[0] only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/window_manager.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/window_manager.py @@ -0,0 +1,53 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +import logging +from autopilot.introspection.types import Rectangle +from autopilot.keybindings import KeybindingsHelper + +from unity.emulators import UnityIntrospectionObject + +logger = logging.getLogger(__name__) + + +class WindowManager(UnityIntrospectionObject, KeybindingsHelper): + """The WindowManager class.""" + + @property + def screen_geometry(self): + """Returns a Rectangle (x,y,w,h) for the screen.""" + return self.globalRect + + def initiate_spread(self): + self.keybinding("spread/start") + self.scale_active.wait_for(True) + + def terminate_spread(self): + self.keybinding("spread/cancel") + self.scale_active.wait_for(False) + + def enter_show_desktop(self): + if not self.showdesktop_active: + logger.info("Entering show desktop mode.") + self.keybinding("window/show_desktop") + self.showdesktop_active.wait_for(True) + else: + logger.warning("Test tried to enter show desktop mode while already \ + in show desktop mode.") + + def leave_show_desktop(self): + if self.showdesktop_active: + logger.info("Leaving show desktop mode.") + self.keybinding("window/show_desktop") + self.showdesktop_active.wait_for(False) + else: + logger.warning("Test tried to leave show desktop mode while not in \ + show desktop mode.") only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/workspace.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/emulators/workspace.py @@ -0,0 +1,98 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +from autopilot.keybindings import KeybindingsHelper +from autopilot.display import Display + +from unity.emulators.compiz import get_compiz_option +from unity.emulators.X11 import get_desktop_viewport + + +class WorkspaceManager(KeybindingsHelper): + """Class to manage switching to different workspaces.""" + + def __init__(self): + super(WorkspaceManager, self).__init__() + self.refresh_workspace_information() + + @property + def num_workspaces(self): + """The number of workspaces configured.""" + return self._workspaces_wide * self._workspaces_high + + @property + def current_workspace(self): + """The current workspace number. 0 <= x < num_workspaces.""" + vx,vy = get_desktop_viewport() + return self._coordinates_to_vp_number(vx, vy) + + def refresh_workspace_information(self): + """Re-read information about available workspaces from compiz and X11.""" + self._viewport_width = 0 + self._viewport_height = 0 + + self._workspaces_wide = get_compiz_option("core", "hsize") + self._workspaces_high = get_compiz_option("core", "vsize") + + display = Display.create() + num_screens = display.get_num_screens() + + for screen in range(num_screens): + _, _, width, height = display.get_screen_geometry(screen) + self._viewport_width += width + self._viewport_height += height + + def switch_to(self, workspace_num): + """Switch to the workspace specified. + + ValueError is raised if workspace_num is outside 0<= workspace_num < num_workspaces. + + """ + if workspace_num < 0 or workspace_num >= self.num_workspaces: + raise ValueError("Workspace number must be between 0 and %d" % self.num_workspaces) + + current_row, current_col = self._vp_number_to_row_col(self.current_workspace) + target_row, target_col = self._vp_number_to_row_col(workspace_num) + lefts = rights = ups = downs = 0 + if current_col > target_col: + lefts = current_col - target_col + else: + rights = target_col - current_col + if current_row > target_row: + ups = current_row - target_row + else: + downs = target_row - current_row + + for i in range(lefts): + self.keybinding("workspace/move_left") + for i in range(rights): + self.keybinding("workspace/move_right") + for i in range(ups): + self.keybinding("workspace/move_up") + for i in range(downs): + self.keybinding("workspace/move_down") + + def _coordinates_to_vp_number(self, vx, vy): + """Translate viewport coordinates to a viewport number.""" + row,col = self._coordinates_to_row_col(vx, vy) + return (row * self._workspaces_wide) + col + + def _coordinates_to_row_col(self, vx, vy): + """Translate viewport coordinates to viewport row,col.""" + row = vy / self._viewport_height + col = vx / self._viewport_width + return (row,col) + + def _vp_number_to_row_col(self, vp_number): + """Translate a viewport number to a viewport row/col.""" + row = vp_number / self._workspaces_wide + col = vp_number % self._workspaces_wide + return (row,col) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/__init__.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/__init__.py @@ -0,0 +1,314 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +"""Autopilot test case class for Unity-specific tests.""" + +from __future__ import absolute_import + + +from codecs import open +from autopilot.introspection import get_proxy_object_for_existing_process +from autopilot.matchers import Eventually +from autopilot.testcase import AutopilotTestCase +from dbus import DBusException +from logging import getLogger +import os +import sys +from tempfile import mktemp +from time import sleep +try: + import windowmocker + import json + HAVE_WINDOWMOCKER=True +except ImportError: + HAVE_WINDOWMOCKER=False +from subprocess import check_output +import time +import tempfile +from testtools.content import text_content +from testtools.matchers import Equals +from unittest.case import SkipTest + +from unity.emulators import ensure_unity_is_running +from unity.emulators.workspace import WorkspaceManager +from unity.emulators.compiz import get_compiz_setting, get_global_context +from unity.emulators.unity import ( + set_log_severity, + start_log_to_file, + reset_logging, + Unity + ) +from unity.emulators.X11 import reset_display + +from Xlib import display +from Xlib import Xutil + +from gi.repository import GLib, Gio + +log = getLogger(__name__) + + +class UnityTestCase(AutopilotTestCase): + """Unity test case base class, with improvments specific to Unity tests.""" + + def setUp(self): + super(UnityTestCase, self).setUp() + try: + ensure_unity_is_running() + except RuntimeError: + log.error("Unity doesn't appear to be running, exiting.") + sys.exit(1) + + self._unity = get_proxy_object_for_existing_process( + connection_name=Unity.DBUS_SERVICE, + object_path=Unity.DBUS_OBJECT, + emulator_base=Unity + ) + + self._setUpUnityLogging() + self._initial_workspace_num = self.workspace.current_workspace + self.addCleanup(self.check_test_behavior) + # + # Setting this here since the show desktop feature seems to be a bit + # ropey. Once it's been proven to work reliably we can remove this line: + self.set_unity_log_level("unity.wm.compiz", "DEBUG") + + # For the length of the test, disable screen locking + self._desktop_settings = Gio.Settings.new("org.gnome.desktop.lockdown") + lock_state = self._desktop_settings.get_boolean("disable-lock-screen") + self._desktop_settings.set_boolean("disable-lock-screen", True) + self.addCleanup(self._desktop_settings.set_boolean, "disable-lock-screen", lock_state) + + def check_test_behavior(self): + """Fail the test if it did something naughty. + + This includes leaving the dash or the hud open, changing the current + workspace, or leaving the system in show_desktop mode. + + """ + well_behaved = True + reasons = [] + log.info("Checking system state for badly behaving test...") + + # Have we switched workspace? + if not self.well_behaved(self.workspace, current_workspace=self._initial_workspace_num): + well_behaved = False + reasons.append("The test changed the active workspace from %d to %d." \ + % (self._initial_workspace_num, self.workspace.current_workspace)) + log.warning("Test changed the active workspace, changing it back...") + self.workspace.switch_to(self._initial_workspace_num) + # Have we left the dash open? + if not self.well_behaved(self.unity.dash, visible=False): + well_behaved = False + reasons.append("The test left the dash open.") + log.warning("Test left the dash open, closing it...") + self.unity.dash.ensure_hidden() + # ... or the hud? + if not self.well_behaved(self.unity.hud, visible=False): + well_behaved = False + reasons.append("The test left the hud open.") + log.warning("Test left the hud open, closing it...") + self.unity.hud.ensure_hidden() + # Are we in show desktop mode? + if not self.well_behaved(self.unity.window_manager, showdesktop_active=False): + well_behaved = False + reasons.append("The test left the system in show_desktop mode.") + log.warning("Test left the system in show desktop mode, exiting it...") + # It is not possible to leave show desktop mode if there are no + # app windows. So, just open a window and perform the show + # desktop action until the desired state is acheived, then close + # the window. The showdesktop_active state will persist. + # + # In the event that this doesn't work, wait_for will throw an + # exception. + win = self.process_manager.start_app_window('Calculator', locale='C') + self.keybinding("window/show_desktop") + count = 1 + while self.unity.window_manager.showdesktop_active: + self.keybinding("window/show_desktop") + sleep(count) + count+=1 + if count > 10: + break + win.close() + self.unity.window_manager.showdesktop_active.wait_for(False) + for launcher in self.unity.launcher.get_launchers(): + if not self.well_behaved(launcher, in_keynav_mode=False): + well_behaved = False + reasons.append("The test left the launcher keynav mode enabled.") + log.warning("Test left the launcher in keynav mode, exiting it...") + launcher.key_nav_cancel() + if not self.well_behaved(launcher, in_switcher_mode=False): + well_behaved = False + reasons.append("The test left the launcher in switcher mode.") + log.warning("Test left the launcher in switcher mode, exiting it...") + launcher.switcher_cancel() + if not self.well_behaved(launcher, quicklist_open=False): + well_behaved = False + reasons.append("The test left a quicklist open.") + log.warning("The test left a quicklist open.") + self.keyboard.press_and_release('Escape') + + if not well_behaved: + self.fail("/n".join(reasons)) + else: + log.info("Test was well behaved.") + + def well_behaved(self, object, **kwargs): + try: + self.assertProperty(object, **kwargs) + except AssertionError: + return False + return True + + @property + def unity(self): + return self._unity + + @property + def workspace(self): + if not getattr(self, '__workspace', None): + self.__workspace = WorkspaceManager() + return self.__workspace + + def _setUpUnityLogging(self): + self._unity_log_file_name = mktemp(prefix=self.shortDescription()) + start_log_to_file(self._unity_log_file_name) + self.addCleanup(self._tearDownUnityLogging) + + def _tearDownUnityLogging(self): + # If unity dies, our dbus interface has gone, and reset_logging will fail + # but we still want our log, so we ignore any errors. + try: + reset_logging() + except DBusException: + pass + with open(self._unity_log_file_name, encoding='utf-8') as unity_log: + self.addDetail('unity-log', text_content(unity_log.read())) + os.remove(self._unity_log_file_name) + self._unity_log_file_name = "" + + def set_unity_log_level(self, component, level): + """Set the unity log level for 'component' to 'level'. + + Valid levels are: TRACE, DEBUG, INFO, WARNING and ERROR. + + Components are dotted unity component names. The empty string specifies + the root logging component. + """ + valid_levels = ('TRACE', 'DEBUG', 'INFO', 'WARN', 'WARNING', 'ERROR') + if level not in valid_levels: + raise ValueError("Log level '%s' must be one of: %r" % (level, valid_levels)) + set_log_severity(component, level) + + def assertNumberWinsIsEventually(self, app, num): + """Asserts that 'app' eventually has 'num' wins. Waits up to 10 seconds.""" + + self.assertThat(lambda: len(app.get_windows()), Eventually(Equals(num))) + + def launch_test_window(self, window_spec={}): + """Launch a test window, for the duration of this test only. + + This uses the 'window-mocker' application, which is not part of the + python-autopilot or unity-autopilot packages. To use this method, you + must have python-windowmocker installed. If the package is not installed, + this method will raise a SkipTest exception, causing the calling test + to be silently skipped. + + window_spec is a list or dictionary that conforms to the window-mocker + specification. + + """ + if not HAVE_WINDOWMOCKER: + raise SkipTest("The python-windowmocker package is required to run this test.") + + if 'Window Mocker' not in self.process_manager.KNOWN_APPS: + self.process_manager.register_known_application( + 'Window Mocker', + 'window-mocker.desktop', + 'window-mocker' + ) + if window_spec: + file_path = tempfile.mktemp() + json.dump(window_spec, open(file_path, 'w')) + self.addCleanup(os.remove, file_path) + return self.process_manager.start_app_window('Window Mocker', [file_path]) + else: + return self.process_manager.start_app_window('Window Mocker') + + def close_all_windows(self, application_name): + for w in self.process_manager.get_open_windows_by_application(application_name): + w.close() + + self.assertThat(lambda: len(self.process_manager.get_open_windows_by_application(application_name)), Eventually(Equals(0))) + + def register_nautilus(self): + self.addCleanup(self.process_manager.unregister_known_application, "Nautilus") + self.process_manager.register_known_application("Nautilus", "org.gnome.Nautilus.desktop", "nautilus") + + def get_startup_notification_timestamp(self, bamf_window): + atom = display.Display().intern_atom('_NET_WM_USER_TIME') + atom_type = display.Display().intern_atom('CARDINAL') + return bamf_window.x_win.get_property(atom, atom_type, 0, 1024).value[0] + + def call_gsettings_cmd(self, command, schema, key, value=None): + """Set a desktop wide gsettings option + """ + settings = Gio.Settings.new(schema) + + if command == "get": + return settings.get_value(key).print_(type_annotate=False) + elif command == "set": + settings.set_value(key, GLib.Variant.parse(type=None, text=value)) + settings.apply() + reset_display() + elif command == "reset": + settings.reset(key) + + def set_unity_option(self, option_name, option_value): + """Set an option in the unity compiz plugin options. + + .. note:: The value will be set for the current test only, and + automatically undone when the test ends. + + :param option_name: The name of the unity option. + :param option_value: The value you want to set. + :raises: **KeyError** if the option named does not exist. + + """ + self.set_compiz_option("unityshell", option_name, option_value) + + def set_compiz_option(self, plugin_name, option_name, option_value): + """Set a compiz option for the duration of this test only. + + .. note:: The value will be set for the current test only, and + automatically undone when the test ends. + + :param plugin_name: The name of the compiz plugin where the option is + registered. If the option is not in a plugin, the string "core" should + be used as the plugin name. + :param option_name: The name of the unity option. + :param option_value: The value you want to set. + :raises: **KeyError** if the option named does not exist. + + """ + old_value = self._set_compiz_option(plugin_name, option_name, option_value) + # Cleanup is LIFO, during clean-up also allow unity to respond + self.addCleanup(time.sleep, 0.5) + self.addCleanup(self._set_compiz_option, plugin_name, option_name, old_value) + # Allow unity time to respond to the new setting. + time.sleep(0.5) + + def _set_compiz_option(self, plugin_name, option_name, option_value): + log.info("Setting compiz option '%s' in plugin '%s' to %r", + option_name, plugin_name, option_value) + setting = get_compiz_setting(plugin_name, option_name) + old_value = setting.Value + setting.Value = option_value + get_global_context().Write() + return old_value only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/__init__.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/__init__.py @@ -0,0 +1,63 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +"""Autopilot test case class for all Launcher tests""" + +from autopilot.display import Display +from autopilot.testcase import multiply_scenarios + +from unity.tests import UnityTestCase +from unity.emulators.X11 import set_primary_monitor + + +def _make_scenarios(): + """Make scenarios for launcher test cases based on the number of configured + monitors. + """ + num_monitors = Display.create().get_num_screens() + + # it doesn't make sense to set only_primary when we're running in a single-monitor setup. + if num_monitors == 1: + return [('Single Monitor', {'launcher_monitor': 0, 'only_primary': False})] + + monitor_scenarios = [('Monitor %d' % (i), {'launcher_monitor': i}) for i in range(num_monitors)] + launcher_mode_scenarios = [('launcher_on_primary', {'only_primary': True}), + ('launcher on all', {'only_primary': False})] + return multiply_scenarios(monitor_scenarios, launcher_mode_scenarios) + + +class LauncherTestCase(UnityTestCase): + """A base class for all launcher tests that uses scenarios to run on + each launcher (for multi-monitor setups). + """ + scenarios = _make_scenarios() + + def setUp(self): + super(LauncherTestCase, self).setUp() + self.set_unity_log_level("unity.launcher", "DEBUG") + self.addCleanup(self.set_unity_log_level, "unity.launcher", "INFO") + + self.set_unity_option('num_launchers', int(self.only_primary)) + self.launcher_instance = self.get_launcher() + + if not self.launcher_instance: + self.skipTest("Cannot run test with no Launcher on monitor %d." % self.launcher_monitor) + + if self.only_primary: + try: + old_primary_screen = self.display.get_primary_screen() + set_primary_monitor(self.launcher_monitor) + self.addCleanup(set_primary_monitor, old_primary_screen) + except Display.BlacklistedDriverError: + self.skipTest("Impossible to set the monitor %d as primary" % self.launcher_monitor) + + self.launcher_instance.move_mouse_to_screen_of_current_launcher() + + def get_launcher(self): + """Get the launcher for the current scenario.""" + return self.unity.launcher.get_launcher_for_monitor(self.launcher_monitor) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_capture.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_capture.py @@ -0,0 +1,130 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Authors: Thomi Richards, +# Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +import logging +from testtools.matchers import Equals, LessThan, GreaterThan +from testtools import skip +from time import sleep + +from unity.tests import UnityTestCase + +logger = logging.getLogger(__name__) + + +class LauncherCaptureTests(UnityTestCase): + """Test the launchers ability to capture/not capture the mouse.""" + + def setUp(self): + super(LauncherCaptureTests, self).setUp() + + if self.display.get_num_screens() <= 1: + self.skipTest("This test requires two or more monitors.") + + self.set_unity_option('launcher_capture_mouse', True) + self.set_unity_option('num_launchers', 0) + self.setHideMode(0) + + def setHideMode(self, mode): + self.set_unity_option('launcher_hide_mode', mode) + launcher = self.unity.launcher.get_launcher_for_monitor(0) + self.assertThat(launcher.hidemode, Eventually(Equals(mode))) + + def leftMostMonitor(self): + x1, y1, width, height = self.display.get_screen_geometry(0) + x2, y2, width, height = self.display.get_screen_geometry(1) + + if x1 < x2: + return 0 + return 1 + + def rightMostMonitor(self): + # TODO: This will break setups with 3 or more monitors. + return 1 - self.leftMostMonitor() + + + @skip("Test is no longer possible due to changes in AP mouse movement.") + def test_launcher_captures_while_sticky_and_revealed(self): + """Tests that the launcher captures the mouse when moving between monitors + while revealed. + """ + x, y, width, height = self.display.get_screen_geometry(self.rightMostMonitor()) + self.mouse.move(x + width / 2, y + height / 2, False) + self.mouse.move(x - width / 2, y + height / 2, True, 5, .002) + + x_fin, y_fin = self.mouse.position() + # The launcher should have held the mouse a little bit + self.assertThat(x_fin, GreaterThan(x - width / 2)) + + def test_launcher_not_capture_while_not_sticky_and_revealed(self): + """Tests that the launcher doesn't captures the mouse when moving between monitors + while revealed and stick is off. + """ + + self.set_unity_option('launcher_capture_mouse', False) + + x, y, width, height = self.display.get_screen_geometry(self.rightMostMonitor()) + self.mouse.move(x + width / 2, y + height / 2, False) + self.mouse.move(x - width / 2, y + height / 2, True, 5, .002) + + x_fin, y_fin = self.mouse.position() + # The launcher should have held the mouse a little bit + self.assertThat(x_fin, Equals(x - width / 2)) + + @skip("Test is no longer possible due to changes in AP mouse movement.") + def test_launcher_capture_while_not_sticky_and_hidden(self): + """Tests that the launcher captures the mouse when moving between monitors + while hidden and sticky is off. (moving left) + """ + + self.set_unity_option('launcher_capture_mouse', False) + self.setHideMode(1) + + x, y, width, height = self.display.get_screen_geometry(self.rightMostMonitor()) + self.mouse.move(x + width / 2, y + height / 2, False) + self.mouse.move(x - width / 2, y + height / 2, True, 5, .002) + + x_fin, y_fin = self.mouse.position() + # The launcher should have held the mouse a little bit + self.assertThat(x_fin, GreaterThan(x - width / 2)) + + def test_launcher_not_capture_while_not_sticky_and_hidden_moving_right(self): + """Tests that the launcher doesn't capture the mouse when moving between monitors + while hidden and sticky is off (moving right). + """ + + self.set_unity_option('launcher_capture_mouse', False) + self.setHideMode(1) + + x, y, width, height = self.display.get_screen_geometry(self.leftMostMonitor()) + self.mouse.move(x + width / 2, y + height / 2, False) + sleep(1.5) + self.mouse.move(x + width * 1.5, y + height / 2, True, 5, .002) + + x_fin, y_fin = self.mouse.position() + # The launcher should have held the mouse a little bit + self.assertThat(x_fin, Equals(x + width * 1.5)) + + @skip("Test is no longer possible due to changes in AP mouse movement.") + def test_launcher_capture_while_sticky_and_hidden_moving_right(self): + """Tests that the launcher captures the mouse when moving between monitors + while hidden. + """ + self.setHideMode(1) + + x, y, width, height = self.display.get_screen_geometry(self.leftMostMonitor()) + self.mouse.move(x + width / 2, y + height / 2, False) + sleep(1.5) + self.mouse.move(x + width * 1.5, y + height / 2, True, 5, .002) + + x_fin, y_fin = self.mouse.position() + # The launcher should have held the mouse a little bit + self.assertThat(x_fin, LessThan(x + width * 1.5)) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_icon_behavior.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_icon_behavior.py @@ -0,0 +1,423 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Authors: Thomi Richards, +# Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +from autopilot.testcase import multiply_scenarios +import logging +from testtools.matchers import Equals, NotEquals, GreaterThan +from time import sleep + +from unity.emulators.icons import ApplicationLauncherIcon, ExpoLauncherIcon +from unity.emulators.launcher import IconDragType +from unity.tests.launcher import LauncherTestCase, _make_scenarios + +from Xlib import Xutil + +logger = logging.getLogger(__name__) + + +class LauncherIconsTests(LauncherTestCase): + """Test the launcher icons interactions""" + + def setUp(self): + super(LauncherIconsTests, self).setUp() + + self.set_unity_option('launcher_hide_mode', 0) + + def ensure_expo_launcher_icon(self): + EXPO_URI = "'unity://expo-icon'" + old_fav = self.call_gsettings_cmd('get', 'com.canonical.Unity.Launcher', 'favorites') + + if not EXPO_URI in old_fav: + if old_fav[:-2] == "[]": + new_fav = "["+EXPO_URI+"]" + else: + new_fav = old_fav[:-1]+", "+EXPO_URI+"]" + + self.addCleanup(self.call_gsettings_cmd, 'set', 'com.canonical.Unity.Launcher', 'favorites', old_fav) + self.call_gsettings_cmd('set', 'com.canonical.Unity.Launcher', 'favorites', new_fav) + + icon = self.unity.launcher.model.get_children_by_type(ExpoLauncherIcon)[0] + self.assertThat(icon, NotEquals(None)) + self.assertThat(icon.visible, Eventually(Equals(True))) + + return icon + + def ensure_calculator_in_launcher_and_not_running(self): + calc = self.process_manager.start_app("Calculator") + calc_icon = self.unity.launcher.model.get_icon(desktop_id=calc.desktop_file) + self.addCleanup(self.launcher_instance.unlock_from_launcher, calc_icon) + self.launcher_instance.lock_to_launcher(calc_icon) + self.process_manager.close_all_app("Calculator") + self.assertThat(lambda: self.process_manager.app_is_running("Calculator"), Eventually(Equals(False))) + return calc_icon + + def get_running_application_by_desktop_file(self, desktop_id): + get_app_fn = lambda: self.process_manager.get_running_applications_by_desktop_file(desktop_id) + self.assertThat(lambda: len(get_app_fn()), Eventually(Equals(1))) + return get_app_fn()[0] + + def test_bfb_tooltip_disappear_when_dash_is_opened(self): + """Tests that the bfb tooltip disappear when the dash is opened.""" + bfb = self.unity.launcher.model.get_bfb_icon() + self.mouse.move(bfb.center.x, bfb.center.y) + + self.assertThat(lambda: bfb.get_tooltip(), Eventually(NotEquals(None))) + self.assertThat(bfb.get_tooltip().active, Eventually(Equals(True))) + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + self.assertThat(bfb.get_tooltip(), Equals(None)) + + def test_bfb_tooltip_is_disabled_when_dash_is_open(self): + """Tests the that bfb tooltip is disabled when the dash is open.""" + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + bfb = self.unity.launcher.model.get_bfb_icon() + self.mouse.move(bfb.center.x, bfb.center.y) + + # Tooltips are lazy-created in Unity, so if the BFB tooltip has never + # been shown before, get_tooltip will return None. If that happens, then + # this test should pass. + tooltip = bfb.get_tooltip() + if tooltip is not None: + self.assertThat(tooltip.active, Eventually(Equals(False))) + + def test_shift_click_opens_new_application_instance(self): + """Shift+Clicking MUST open a new instance of an already-running application.""" + app = self.process_manager.start_app("Calculator") + icon = self.unity.launcher.model.get_icon(desktop_id=app.desktop_file) + + self.keyboard.press("Shift") + self.addCleanup(self.keyboard.release, "Shift") + self.launcher_instance.click_launcher_icon(icon) + + self.assertNumberWinsIsEventually(app, 2) + + def test_launcher_activate_last_focused_window(self): + """Activating a launcher icon must raise only the last focused instance + of that application. + + """ + char_win1 = self.process_manager.start_app_window("Character Map") + calc_win = self.process_manager.start_app_window("Calculator") + char_win2 = self.process_manager.start_app_window("Character Map") + + self.assertVisibleWindowStack([char_win2, calc_win, char_win1]) + + char_icon = self.unity.launcher.model.get_icon( + desktop_id=char_win2.application.desktop_file) + calc_icon = self.unity.launcher.model.get_icon( + desktop_id=calc_win.application.desktop_file) + + self.launcher_instance.click_launcher_icon(calc_icon) + self.assertProperty(calc_win, is_focused=True) + self.assertVisibleWindowStack([calc_win, char_win2, char_win1]) + + self.launcher_instance.click_launcher_icon(char_icon) + self.assertProperty(char_win2, is_focused=True) + self.assertVisibleWindowStack([char_win2, calc_win, char_win1]) + + self.keybinding("window/minimize") + + self.assertThat(lambda: char_win2.is_hidden, Eventually(Equals(True))) + self.assertProperty(calc_win, is_focused=True) + self.assertVisibleWindowStack([calc_win, char_win1]) + + self.launcher_instance.click_launcher_icon(char_icon) + self.assertProperty(char_win1, is_focused=True) + self.assertThat(lambda: char_win2.is_hidden, Eventually(Equals(True))) + self.assertVisibleWindowStack([char_win1, calc_win]) + + def test_launcher_uses_startup_notification(self): + """Tests that unity uses startup notification protocol.""" + calc_icon = self.ensure_calculator_in_launcher_and_not_running() + self.addCleanup(self.process_manager.close_all_app, "Calculator") + self.launcher_instance.click_launcher_icon(calc_icon) + + calc_app = self.get_running_application_by_desktop_file(calc_icon.desktop_id) + calc_window = calc_app.get_windows()[0] + + self.assertThat(lambda: self.get_startup_notification_timestamp(calc_window), Eventually(Equals(calc_icon.startup_notification_timestamp))) + + def test_trash_icon_refocus_opened_instance(self): + """Tests that when the trash is opened, clicking on the icon re-focus the trash again""" + self.register_nautilus() + self.addCleanup(self.close_all_windows, "Nautilus") + self.addCleanup(self.process_manager.close_all_app, "Calculator") + self.close_all_windows("Nautilus") + + trash_icon = self.unity.launcher.model.get_trash_icon() + self.launcher_instance.click_launcher_icon(trash_icon) + self.assertThat(lambda: len(self.process_manager.get_open_windows_by_application("Nautilus")), Eventually(Equals(1))) + [trash_window] = self.process_manager.get_open_windows_by_application("Nautilus") + self.assertThat(lambda: trash_window.is_focused, Eventually(Equals(True))) + + calc_win = self.process_manager.start_app_window("Calculator") + self.assertThat(lambda: calc_win.is_focused, Eventually(Equals(True))) + self.assertThat(lambda: trash_window.is_focused, Eventually(Equals(False))) + + self.launcher_instance.click_launcher_icon(trash_icon) + self.assertThat(lambda: trash_window.is_focused, Eventually(Equals(True))) + + def test_trash_open_does_not_prevent_nautilus_to_run(self): + """Tests that when the trash is opened, launching still opens a new window""" + self.register_nautilus() + self.addCleanup(self.close_all_windows, "Nautilus") + self.close_all_windows("Nautilus") + + trash_icon = self.unity.launcher.model.get_trash_icon() + self.launcher_instance.click_launcher_icon(trash_icon) + self.assertThat(lambda: len(self.process_manager.get_open_windows_by_application("Nautilus")), Eventually(Equals(1))) + + nautilus_app = self.process_manager.get_app_instances("Nautilus") + nautilus_icon = self.unity.launcher.model.get_icon(desktop_id="org.gnome.Nautilus.desktop") + self.launcher_instance.click_launcher_icon(nautilus_icon) + self.assertThat(lambda: len(self.process_manager.get_open_windows_by_application("Nautilus")), Eventually(Equals(2))) + + def test_super_number_shortcut_focuses_new_windows(self): + """Windows launched using super+number must have + keyboard focus. + + """ + bfb_icon = self.unity.launcher.model.get_bfb_icon() + calc_icon = self.ensure_calculator_in_launcher_and_not_running() + self.addCleanup(self.process_manager.close_all_app, "Calculator") + + self.launcher_instance.drag_icon_to_position( + calc_icon, + IconDragType.AFTER, + bfb_icon) + + self.launcher_instance.keyboard_reveal_launcher() + self.addCleanup(self.launcher_instance.keyboard_unreveal_launcher) + self.keyboard.press_and_release("1"); + + calc_app = self.get_running_application_by_desktop_file(calc_icon.desktop_id) + [calc_window] = calc_app.get_windows() + + self.assertThat(lambda: calc_window.is_focused, Eventually(Equals(True))) + + def test_clicking_icon_twice_initiates_spread(self): + """This tests shows that when you click on a launcher icon twice, + when an application window is focused, the spread is initiated. + """ + char_win1 = self.process_manager.start_app_window("Character Map") + char_win2 = self.process_manager.start_app_window("Character Map") + char_app = char_win1.application + + self.assertVisibleWindowStack([char_win2, char_win1]) + self.assertProperty(char_win2, is_focused=True) + + char_icon = self.unity.launcher.model.get_icon(desktop_id=char_app.desktop_file) + self.addCleanup(self.keybinding, "spread/cancel") + self.launcher_instance.click_launcher_icon(char_icon) + + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) + self.assertThat(self.unity.window_manager.scale_active_for_group, Eventually(Equals(True))) + + def test_while_in_scale_mode_the_dash_will_still_open(self): + """If scale is initiated through the laucher pressing super must close + scale and open the dash. + """ + char_win1 = self.process_manager.start_app_window("Character Map") + char_win2 = self.process_manager.start_app_window("Character Map") + char_app = char_win1.application + + self.assertVisibleWindowStack([char_win2, char_win1]) + self.assertProperty(char_win2, is_focused=True) + + char_icon = self.unity.launcher.model.get_icon(desktop_id=char_app.desktop_file) + self.launcher_instance.click_launcher_icon(char_icon) + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) + + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(False))) + + def test_right_click_on_icon_ends_expo(self): + """Right click on a launcher icon in expo mode must end the expo + and show the quicklist. + + """ + if self.workspace.num_workspaces <= 1: + self.skipTest("This test requires enabled workspaces.") + + self.keybinding("expo/start") + self.assertThat(self.unity.window_manager.expo_active, Eventually(Equals(True))) + self.addCleanup(self.keybinding, "expo/cancel") + + bfb = self.unity.launcher.model.get_bfb_icon() + self.mouse.move(bfb.center.x, bfb.center.y) + self.mouse.click(button=3) + + self.assertThat(self.launcher_instance.quicklist_open, Eventually(Equals(True))) + self.assertThat(self.unity.window_manager.expo_active, Eventually(Equals(False))) + + def test_expo_launcher_icon_initiates_expo(self): + """Clicking on the expo launcher icon must start the expo.""" + if self.workspace.num_workspaces <= 1: + self.skipTest("This test requires enabled workspaces.") + + expo = self.ensure_expo_launcher_icon() + self.addCleanup(self.keybinding, "expo/cancel") + self.launcher_instance.click_launcher_icon(expo) + + self.assertThat(self.unity.window_manager.expo_active, Eventually(Equals(True))) + + def test_expo_launcher_icon_terminates_expo(self): + """Clicking on the expo launcher icon when expo is active must close it.""" + if self.workspace.num_workspaces <= 1: + self.skipTest("This test requires enabled workspaces.") + + self.keybinding("expo/start") + self.assertThat(self.unity.window_manager.expo_active, Eventually(Equals(True))) + self.addCleanup(self.keybinding, "expo/cancel") + + expo = self.ensure_expo_launcher_icon() + self.launcher_instance.click_launcher_icon(expo) + + self.assertThat(self.unity.window_manager.expo_active, Eventually(Equals(False))) + + def test_unminimize_initially_minimized_windows(self): + """Make sure initially minimized windows can be unminimized.""" + window_spec = { + "Title": "Hello", + "Minimized": True + } + + window = self.launch_test_window(window_spec) + icon = self.unity.launcher.model.get_icon(desktop_id=window.application.desktop_file) + + self.launcher_instance.click_launcher_icon(icon) + self.assertThat(lambda: window.x_win.get_wm_state()['state'], Eventually(Equals(Xutil.NormalState))) + + def test_unminimize_minimized_immediately_after_show_windows(self): + """Make sure minimized-immediately-after-show windows can be unminimized.""" + window_spec = { + "Title": "Hello", + "MinimizeImmediatelyAfterShow": True + } + + window = self.launch_test_window(window_spec) + icon = self.unity.launcher.model.get_icon(desktop_id=window.application.desktop_file) + + self.launcher_instance.click_launcher_icon(icon) + self.assertThat(lambda: window.x_win.get_wm_state()['state'], Eventually(Equals(Xutil.NormalState))) + + def test_icon_focuses_application_window_when_dash_is_open(self): + """Clicking on an icon when dash is shown must focus it.""" + win = self.process_manager.start_app_window("Calculator") + self.assertThat(lambda: win.is_focused, Eventually(Equals(True))) + + self.addCleanup(self.unity.dash.ensure_hidden) + self.unity.dash.ensure_visible() + self.assertThat(lambda: win.is_focused, Eventually(Equals(False))) + + icon = self.unity.launcher.model.get_icon(desktop_id=win.application.desktop_file) + self.launcher_instance.click_launcher_icon(icon) + + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + self.assertThat(lambda: win.is_focused, Eventually(Equals(True))) + + def test_icon_focuses_application_window_when_hud_is_open(self): + """Clicking on an icon when hud is shown must focus it.""" + win = self.process_manager.start_app_window("Calculator") + self.assertThat(lambda: win.is_focused, Eventually(Equals(True))) + + self.addCleanup(self.unity.hud.ensure_hidden) + self.unity.hud.ensure_visible() + self.assertThat(lambda: win.is_focused, Eventually(Equals(False))) + + icon = self.unity.launcher.model.get_icon(desktop_id=win.application.desktop_file) + self.launcher_instance.click_launcher_icon(icon) + + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + self.assertThat(lambda: win.is_focused, Eventually(Equals(True))) + + +class LauncherDragIconsBehavior(LauncherTestCase): + """Tests dragging icons around the Launcher.""" + + scenarios = multiply_scenarios(_make_scenarios(), + [ + ('inside', {'drag_type': IconDragType.INSIDE}), + ('outside', {'drag_type': IconDragType.OUTSIDE}), + ]) + + def setUp(self): + super(LauncherDragIconsBehavior, self).setUp() + + self.set_unity_option('launcher_hide_mode', 0) + + def ensure_calc_icon_not_in_launcher(self): + """Wait until the launcher model updates and removes the calc icon.""" + # Normally we'd use get_icon(desktop_id="...") but we're expecting it to + # not exist, and we don't want to wait for 10 seconds, so we do this + # the old fashioned way. + get_icon_fn = lambda: self.unity.launcher.model.get_children_by_type( + ApplicationLauncherIcon, desktop_id="gcalctool.desktop") + calc_icon = get_icon_fn() + if calc_icon: + self.launcher_instance.unlock_from_launcher(calc_icon[0]) + + self.assertThat(get_icon_fn, Eventually(Equals([]))) + + def test_can_drag_icon_below_bfb(self): + """Application icons must be draggable to below the BFB.""" + + self.ensure_calc_icon_not_in_launcher() + calc = self.process_manager.start_app("Calculator") + calc_icon = self.unity.launcher.model.get_icon(desktop_id=calc.desktop_file) + bfb_icon = self.unity.launcher.model.get_bfb_icon() + + self.launcher_instance.drag_icon_to_position( + calc_icon, + IconDragType.AFTER, + bfb_icon, + self.drag_type) + moved_icon = self.unity.launcher.model.\ + get_launcher_icons_for_monitor(self.launcher_monitor)[1] + self.assertThat(moved_icon, Equals(calc_icon)) + + def test_can_drag_icon_below_window_switcher(self): + """Application icons must be dragable to below the workspace switcher icon.""" + + self.ensure_calc_icon_not_in_launcher() + calc = self.process_manager.start_app("Calculator") + calc_icon = self.unity.launcher.model.get_icon(desktop_id=calc.desktop_file) + bfb_icon = self.unity.launcher.model.get_bfb_icon() + trash_icon = self.unity.launcher.model.get_trash_icon() + + # Move a known icon to the top as it needs to be more than 2 icon + # spaces away for this test to actually do anything + self.launcher_instance.drag_icon_to_position( + calc_icon, + IconDragType.AFTER, + bfb_icon, + self.drag_type) + + sleep(1) + self.launcher_instance.drag_icon_to_position( + calc_icon, + IconDragType.BEFORE, + trash_icon, + self.drag_type) + + # Must be the last bamf icon - not necessarily the third-from-end icon. + expected_pos = -2 if self.workspace.num_workspaces < 2 else -1 + refresh_fn = lambda: self.unity.launcher.model.get_launcher_icons()[expected_pos].id + self.assertThat(refresh_fn, + Eventually(Equals(calc_icon.id)), + "Launcher icons are: %r" % self.unity.launcher.model.get_launcher_icons()) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_keynav.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_keynav.py @@ -0,0 +1,252 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Authors: Thomi Richards, +# Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +import logging +from testtools.matchers import Equals, GreaterThan + +from unity.tests.launcher import LauncherTestCase + +logger = logging.getLogger(__name__) + + +class LauncherKeyNavTests(LauncherTestCase): + """Test the launcher key navigation""" + + def start_keynav_with_cleanup_cancel(self): + """Start keynav mode safely. + + This adds a cleanup action that cancels keynav mode at the end of the + test if it's still running (but does nothing otherwise). + + """ + self.launcher_instance.key_nav_start() + self.addCleanup(self.safe_quit_keynav) + + def safe_quit_keynav(self): + """Quit the keynav mode if it's engaged.""" + if self.unity.launcher.key_nav_is_active: + self.launcher_instance.key_nav_cancel() + + def test_launcher_keynav_initiate(self): + """Tests we can initiate keyboard navigation on the launcher.""" + self.start_keynav_with_cleanup_cancel() + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(True))) + self.assertThat(self.unity.launcher.key_nav_is_grabbed, Eventually(Equals(True))) + + def test_launcher_keynav_cancel(self): + """Test that we can exit keynav mode.""" + self.launcher_instance.key_nav_start() + self.launcher_instance.key_nav_cancel() + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) + self.assertThat(self.unity.launcher.key_nav_is_grabbed, Eventually(Equals(False))) + + def test_launcher_keynav_cancel_resume_focus(self): + """Test that ending the launcher keynav resume the focus.""" + calc = self.process_manager.start_app("Calculator") + self.assertTrue(calc.is_active) + + self.start_keynav_with_cleanup_cancel() + self.assertFalse(calc.is_active) + + self.launcher_instance.key_nav_cancel() + self.assertTrue(calc.is_active) + + def test_launcher_keynav_starts_at_index_zero(self): + """Test keynav mode starts at index 0.""" + self.start_keynav_with_cleanup_cancel() + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(Equals(0))) + + def test_launcher_keynav_forward(self): + """Must be able to move forwards while in keynav mode.""" + self.start_keynav_with_cleanup_cancel() + self.launcher_instance.key_nav_next() + # The launcher model has hidden items, so the keynav indexes do not + # increase by 1 each time. This test was failing because the 2nd icon + # had an index of 2, not 1 as expected. The best we can do here is to + # make sure that the index has increased. This opens us to the + # possibility that the launcher really is skipping forward more than one + # icon at a time, but we can't do much about that. + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(0))) + + def test_launcher_keynav_prev_works(self): + """Must be able to move backwards while in keynav mode.""" + self.start_keynav_with_cleanup_cancel() + self.launcher_instance.key_nav_next() + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(0))) + self.launcher_instance.key_nav_prev() + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(Equals(0))) + + def test_launcher_keynav_cycling_forward(self): + """Launcher keynav must loop through icons when cycling forwards""" + self.start_keynav_with_cleanup_cancel() + prev_icon = 0 + for icon in range(1, self.unity.launcher.model.num_launcher_icons()): + self.launcher_instance.key_nav_next() + # FIXME We can't directly check for selection/icon number equalty + # since the launcher model also contains "hidden" icons that aren't + # shown, so the selection index can increment by more than 1. + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(prev_icon))) + prev_icon = self.unity.launcher.key_nav_selection + + self.launcher_instance.key_nav_next() + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(Equals(0))) + + def test_launcher_keynav_cycling_backward(self): + """Launcher keynav must loop through icons when cycling backwards""" + self.start_keynav_with_cleanup_cancel() + self.launcher_instance.key_nav_prev() + # FIXME We can't directly check for self.unity.launcher.num_launcher_icons - 1 + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(1))) + + def test_launcher_keynav_can_open_and_close_quicklist(self): + """Tests that we can open and close a quicklist from keynav mode.""" + self.start_keynav_with_cleanup_cancel() + self.launcher_instance.key_nav_next() + self.addCleanup(self.keyboard.press_and_release, "Escape") + self.launcher_instance.key_nav_enter_quicklist() + self.assertThat(self.launcher_instance.quicklist_open, Eventually(Equals(True))) + self.launcher_instance.key_nav_exit_quicklist() + self.assertThat(self.launcher_instance.quicklist_open, Eventually(Equals(False))) + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(True))) + self.assertThat(self.unity.launcher.key_nav_is_grabbed, Eventually(Equals(True))) + + def test_launcher_keynav_mode_toggles(self): + """Tests that keynav mode toggles with Alt+F1.""" + # was initiated in setup. + self.start_keynav_with_cleanup_cancel() + self.keybinding("launcher/keynav") + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) + + def test_launcher_keynav_activate_keep_focus(self): + """Activating a running launcher icon must focus it.""" + calc = self.process_manager.start_app("Calculator") + mahjongg = self.process_manager.start_app("Mahjongg") + self.assertTrue(mahjongg.is_active) + self.assertFalse(calc.is_active) + + self.start_keynav_with_cleanup_cancel() + + self.launcher_instance.keyboard_select_icon(tooltip_text=calc.name) + self.launcher_instance.key_nav_activate() + + self.assertTrue(calc.is_active) + self.assertFalse(mahjongg.is_active) + + def test_launcher_keynav_expo_focus(self): + """When entering expo mode from KeyNav the Desktop must get focus.""" + if self.workspace.num_workspaces < 2: + self.skipTest("This test requires enabled more than one workspace.") + + self.start_keynav_with_cleanup_cancel() + + self.launcher_instance.keyboard_select_icon(tooltip_text="Workspace Switcher") + self.launcher_instance.key_nav_activate() + self.addCleanup(self.keybinding, "expo/cancel") + + self.assertThat(self.unity.panels.get_active_panel().title, Eventually(Equals("Ubuntu Desktop"))) + + def test_launcher_keynav_expo_exit_on_esc(self): + """Esc should quit expo when entering it from KeyNav.""" + if self.workspace.num_workspaces < 2: + self.skipTest("This test requires enabled more than one workspace.") + self.start_keynav_with_cleanup_cancel() + + self.launcher_instance.keyboard_select_icon(tooltip_text="Workspace Switcher") + self.launcher_instance.key_nav_activate() + + self.keyboard.press_and_release("Escape") + self.assertThat(self.unity.window_manager.expo_active, Eventually(Equals(False))) + + def test_launcher_keynav_alt_tab_quits(self): + """Tests that alt+tab exits keynav mode.""" + self.start_keynav_with_cleanup_cancel() + + self.keybinding("switcher/reveal_normal") + self.addCleanup(self.unity.switcher.terminate) + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) + + def test_launcher_keynav_alt_grave_quits(self): + """Tests that alt+` exits keynav mode.""" + self.start_keynav_with_cleanup_cancel() + # Can't use switcher emulat here since the switcher won't appear. + self.keybinding("switcher/reveal_details") + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) + + def test_launcher_keynav_cancel_doesnt_activate_icon(self): + """This tests when canceling keynav the current icon doesnt activate.""" + self.start_keynav_with_cleanup_cancel() + self.keyboard.press_and_release("Escape") + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + + def test_alt_f1_closes_dash(self): + """Pressing Alt+F1 when the Dash is open must close the Dash and start keynav.""" + self.unity.dash.ensure_visible() + + self.start_keynav_with_cleanup_cancel() + + self.assertThat(self.unity.dash.visible, Equals(False)) + self.assertThat(self.unity.launcher.key_nav_is_active, Equals(True)) + + def test_alt_f1_closes_hud(self): + """Pressing Alt+F1 when the HUD is open must close the HUD and start keynav.""" + self.unity.hud.ensure_visible() + + self.start_keynav_with_cleanup_cancel() + + self.assertThat(self.unity.hud.visible, Equals(False)) + self.assertThat(self.unity.launcher.key_nav_is_active, Equals(True)) + + def test_launcher_keynav_cancel_on_click_outside(self): + """A single click outside of launcher must cancel keynav.""" + self.start_keynav_with_cleanup_cancel() + + self.launcher_instance.move_mouse_to_right_of_launcher() + self.mouse.click() + + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) + + def test_launcher_keynav_cancel_on_click_icon(self): + """A single click on a launcher icon must cancel keynav.""" + calc_win = self.process_manager.start_app_window('Calculator', locale = 'C') + calc_app = calc_win.application + calc_icon = self.unity.launcher.model.get_icon(desktop_id=calc_app.desktop_file) + + self.start_keynav_with_cleanup_cancel() + + self.launcher_instance.click_launcher_icon(calc_icon) + + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) + + def test_launcher_keynav_cancel_on_quicklist_activate(self): + """A single click on a quicklist item must cancel keynav.""" + self.start_keynav_with_cleanup_cancel() + self.launcher_instance.key_nav_enter_quicklist() + + bfb_icon = self.unity.launcher.model.get_bfb_icon() + bfb_ql = bfb_icon.get_quicklist() + + bfb_ql.click_item(bfb_ql.selected_item) + self.addCleanup(self.unity.dash.ensure_hidden) + + self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) + + def test_launcher_keynav_changes_panel(self): + """The panel title must change when in key nav mode + and LIM is not enabled. + """ + + self.start_keynav_with_cleanup_cancel() + panel = self.unity.panels.get_active_panel() + expected_panel_title = ("" if panel.menus.integrated_menus + else "Search your computer and online sources") + self.assertThat(panel.title, Eventually(Equals(expected_panel_title))) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_reveal.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_reveal.py @@ -0,0 +1,116 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Authors: Thomi Richards, +# Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.display import move_mouse_to_screen +from autopilot.matchers import Eventually +import logging +from testtools.matchers import Equals, GreaterThan +from time import sleep + +from unity.tests.launcher import LauncherTestCase + +logger = logging.getLogger(__name__) + + +class LauncherRevealTests(LauncherTestCase): + """Test the launcher reveal behavior when in autohide mode.""" + + def setUp(self): + super(LauncherRevealTests, self).setUp() + # these automatically reset to the original value, as implemented in AutopilotTestCase + self.set_unity_option('launcher_capture_mouse', True) + self.set_unity_option('launcher_hide_mode', 1) + launcher = self.get_launcher() + self.assertThat(launcher.hidemode, Eventually(Equals(1))) + + def test_launcher_keyboard_reveal_works(self): + """Revealing launcher with keyboard must work.""" + self.launcher_instance.keyboard_reveal_launcher() + self.addCleanup(self.launcher_instance.keyboard_unreveal_launcher) + self.assertThat(self.launcher_instance.is_showing, Eventually(Equals(True))) + + def test_reveal_on_mouse_to_edge(self): + """Tests reveal of launchers by mouse pressure.""" + self.launcher_instance.move_mouse_to_right_of_launcher() + self.launcher_instance.mouse_reveal_launcher() + self.assertThat(self.launcher_instance.is_showing, Eventually(Equals(True))) + + def test_reveal_with_mouse_under_launcher(self): + """The Launcher must hide properly if the mouse is under the launcher.""" + + self.launcher_instance.move_mouse_over_launcher() + # we can't use "launcher_instance.keyboard_reveal_launcher()" + # since it moves the mouse out of the way, invalidating the test. + self.keybinding_hold("launcher/reveal") + sleep(1) + self.keybinding_release("launcher/reveal") + self.assertThat(self.launcher_instance.is_showing, Eventually(Equals(False))) + + def test_reveal_does_not_hide_again(self): + """Tests reveal of launchers by mouse pressure to ensure it doesn't + automatically hide again. + """ + self.launcher_instance.move_mouse_to_right_of_launcher() + self.launcher_instance.mouse_reveal_launcher() + self.assertThat(self.launcher_instance.is_showing, Eventually(Equals(True))) + + def test_launcher_does_not_reveal_with_mouse_down(self): + """Launcher must not reveal if have mouse button 1 down.""" + move_mouse_to_screen(self.launcher_instance.monitor) + self.mouse.press(1) + self.addCleanup(self.mouse.release, 1) + #FIXME: This is really bad API. it says reveal but it's expected to fail. bad bad bad!! + self.launcher_instance.mouse_reveal_launcher() + # Need a sleep here otherwise this test would pass even if the code failed. + # THis test needs to be rewritten... + sleep(5) + self.assertThat(self.launcher_instance.is_showing, Equals(False)) + + def test_launcher_stays_open_after_spread(self): + """Clicking on the launcher to close an active spread must not hide the launcher.""" + char_win1 = self.process_manager.start_app_window("Character Map") + char_win2 = self.process_manager.start_app_window("Character Map") + char_app = char_win1.application + + char_icon = self.unity.launcher.model.get_icon(desktop_id=char_app.desktop_file) + + self.launcher_instance.click_launcher_icon(char_icon, move_mouse_after=False) + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) + self.launcher_instance.click_launcher_icon(char_icon, move_mouse_after=False) + + self.assertThat(self.launcher_instance.is_showing, Eventually(Equals(True))) + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(False))) + + def test_launcher_stays_open_after_icon_click(self): + """Clicking on a launcher icon must not hide the launcher.""" + char_win = self.process_manager.start_app_window("Character Map") + char_app = char_win.application + + char_icon = self.unity.launcher.model.get_icon(desktop_id=char_app.desktop_file) + self.launcher_instance.click_launcher_icon(char_icon, move_mouse_after=False) + + # Have to sleep to give the launcher time to hide (what the old behavior was) + sleep(5) + + self.assertThat(self.launcher_instance.is_showing, Eventually(Equals(True))) + + def test_new_icon_has_the_shortcut(self): + """New icons should have an associated shortcut""" + if self.unity.launcher.model.num_bamf_launcher_icons() >= 10: + self.skip("There are already more than 9 icons in the launcher") + + desktop_file = self.process_manager.KNOWN_APPS['Calculator']['desktop-file'] + if self.unity.launcher.model.get_icon(desktop_id=desktop_file) != None: + self.skip("Calculator icon is already on the launcher.") + + self.process_manager.start_app('Calculator') + icon = self.unity.launcher.model.get_icon(desktop_id=desktop_file) + self.assertThat(icon.shortcut, GreaterThan(0)) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_scroll.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_scroll.py @@ -0,0 +1,101 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2013 Canonical +# Authors: Chris Townsend +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +import logging +from testtools.matchers import Equals, GreaterThan, LessThan +from time import sleep + +from unity.tests.launcher import LauncherTestCase + +logger = logging.getLogger(__name__) + +class LauncherScrollTests(LauncherTestCase): + """Tests for scrolling behavior of the Launcher""" + + def open_apps_in_launcher(self): + """Opens some apps in order to get icon stacking in the Launcher""" + + # Add some additional applications, since we need a lot of those on big screens + if "System Monitor" not in self.process_manager.KNOWN_APPS: + self.process_manager.register_known_application("System Monitor", "gnome-system-monitor.desktop", "gnome-system-monitor") + if "Archive Manager" not in self.process_manager.KNOWN_APPS: + self.process_manager.register_known_application("Archive Manager", "org.gnome.FileRoller.desktop", "file-roller") + + apps = ("Calculator", "Mahjongg", "Text Editor", "Character Map", "Terminal", "Remmina", "System Monitor", "Archive Manager") + + for app in apps: + self.process_manager.start_app_window(app) + + def test_autoscrolling_from_bottom(self): + """Tests the autoscrolling from the bottom of the Launcher""" + self.open_apps_in_launcher() + + # Set the autoscroll_offset to 10 (this is arbitrary for this test). + autoscroll_offset = 10 + + launcher_instance = self.get_launcher() + (x, y, w, h) = launcher_instance.geometry + + icons = self.unity.launcher.model.get_launcher_icons_for_monitor(self.launcher_monitor) + num_icons = self.unity.launcher.model.num_launcher_icons() + + last_icon = icons[num_icons - 1] + + # Move mouse to the middle of the Launcher in order to expand all + # of the icons for scrolling + launcher_instance.move_mouse_over_launcher() + + # Make sure the last icon is off the screen or else there is no + # scrolling. + self.assertThat(last_icon.center.y, GreaterThan(h)) + + # Autoscroll to the last icon + launcher_instance.move_mouse_to_icon(last_icon, autoscroll_offset) + + (x_fin, y_fin) = self.mouse.position() + + # Make sure we ended up in the center of the last icon + self.assertThat(x_fin, Equals(last_icon.center.x)) + self.assertThat(y_fin, Equals(last_icon.center.y)) + + def test_autoscrolling_from_top(self): + """Test the autoscrolling from the top of the Launcher""" + self.open_apps_in_launcher() + + # Set the autoscroll_offset to 10 (this is arbitrary for this test). + autoscroll_offset = 10 + + launcher_instance = self.get_launcher() + (x, y, w, h) = launcher_instance.geometry + + icons = self.unity.launcher.model.get_launcher_icons_for_monitor(self.launcher_monitor) + num_icons = self.unity.launcher.model.num_launcher_icons() + + first_icon = icons[0] + last_icon = icons[num_icons - 1] + + launcher_instance.move_mouse_over_launcher() + + # Move to the last icon in order to expand the top of the Launcher + launcher_instance.move_mouse_to_icon(last_icon) + + # Make sure the first icon is off the screen or else there is no + # scrolling. + self.assertThat(first_icon.center.y, LessThan(y)) + + # Autoscroll to the first icon + launcher_instance.move_mouse_to_icon(first_icon, autoscroll_offset) + + (x_fin, y_fin) = self.mouse.position() + + # Make sure we ended up in the center of the first icon + self.assertThat(x_fin, Equals(first_icon.center.x)) + self.assertThat(y_fin, Equals(first_icon.center.y)) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_shortcut.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_shortcut.py @@ -0,0 +1,61 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Authors: Thomi Richards, +# Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +import logging +from testtools.matchers import Equals +from time import sleep + +from unity.tests.launcher import LauncherTestCase + +logger = logging.getLogger(__name__) + + +class LauncherShortcutTests(LauncherTestCase): + """Tests for the shortcut hint window.""" + + def setUp(self): + super(LauncherShortcutTests, self).setUp() + self.launcher_instance.keyboard_reveal_launcher() + self.addCleanup(self.launcher_instance.keyboard_unreveal_launcher) + sleep(2) + + def test_launcher_keyboard_reveal_shows_shortcut_hints(self): + """Launcher icons must show shortcut hints after revealing with keyboard.""" + self.assertThat(self.launcher_instance.shortcuts_shown, Eventually(Equals(True))) + + def test_launcher_switcher_keeps_shorcuts(self): + """Initiating launcher switcher after showing shortcuts must not hide shortcuts""" + self.launcher_instance.switcher_start() + self.addCleanup(self.launcher_instance.switcher_cancel) + + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(True))) + self.assertThat(self.launcher_instance.shortcuts_shown, Eventually(Equals(True))) + + def test_launcher_switcher_next_keeps_shortcuts(self): + """Launcher switcher next action must keep shortcuts after they've been shown.""" + self.launcher_instance.switcher_start() + self.addCleanup(self.launcher_instance.switcher_cancel) + self.launcher_instance.switcher_next() + self.assertThat(self.launcher_instance.shortcuts_shown, Eventually(Equals(True))) + + def test_launcher_switcher_prev_keeps_shortcuts(self): + """Launcher switcher prev action must keep shortcuts after they've been shown.""" + self.launcher_instance.switcher_start() + self.addCleanup(self.launcher_instance.switcher_cancel) + self.launcher_instance.switcher_prev() + self.assertThat(self.launcher_instance.shortcuts_shown, Eventually(Equals(True))) + + def test_tooltip_not_shown(self): + """Tooltip must not be shown after revealing the launcher with keyboard + and mouse is not on the launcher. + """ + self.assertThat(self.launcher_instance.tooltip_shown, Eventually(Equals(False))) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_switcher.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_switcher.py @@ -0,0 +1,168 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Authors: Thomi Richards, +# Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually + +import logging +from testtools.matchers import Equals, NotEquals, GreaterThan +from time import sleep + +from unity.tests.launcher import LauncherTestCase + + +logger = logging.getLogger(__name__) + + +class LauncherSwitcherTests(LauncherTestCase): + """ Tests the functionality of the launcher's switcher capability""" + + def start_switcher_with_cleanup_cancel(self): + """Start switcher mode safely. + + This adds a cleanup action that cancels keynav mode at the end of the + test if it's still running (but does nothing otherwise). + + """ + self.launcher_instance.switcher_start() + self.addCleanup(self.safe_quit_switcher) + + def safe_quit_switcher(self): + """Quit the keynav mode if it's engaged.""" + if self.unity.launcher.key_nav_is_active: + self.launcher_instance.switcher_cancel() + + def test_launcher_switcher_cancel(self): + """Test that ending the launcher switcher actually works.""" + self.launcher_instance.switcher_start() + self.launcher_instance.switcher_cancel() + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) + + def test_launcher_switcher_cancel_resume_focus(self): + """Test that ending the launcher switcher resume the focus.""" + self.process_manager.close_all_app("Calculator") + calc = self.process_manager.start_app("Calculator") + self.assertTrue(calc.is_active) + + self.start_switcher_with_cleanup_cancel() + sleep(.5) + self.assertFalse(calc.is_active) + + self.launcher_instance.switcher_cancel() + sleep(.5) + self.assertTrue(calc.is_active) + + def test_launcher_switcher_starts_at_index_zero(self): + """Test that starting the Launcher switcher puts the keyboard focus on item 0.""" + self.start_switcher_with_cleanup_cancel() + + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(True))) + self.assertThat(self.unity.launcher.key_nav_is_grabbed, Eventually(Equals(False))) + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(Equals(0))) + + def test_launcher_switcher_next(self): + """Moving to the next launcher item while switcher is activated must work.""" + self.start_switcher_with_cleanup_cancel() + self.launcher_instance.switcher_next() + # The launcher model has hidden items, so the keynav indexes do not + # increase by 1 each time. This test was failing because the 2nd icon + # had an index of 2, not 1 as expected. The best we can do here is to + # make sure that the index has increased. This opens us to the + # possibility that the launcher really is skipping forward more than one + # icon at a time, but we can't do much about that. + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(0))) + + def test_launcher_switcher_prev(self): + """Moving to the previous launcher item while switcher is activated must work.""" + self.start_switcher_with_cleanup_cancel() + self.launcher_instance.switcher_prev() + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(NotEquals(0))) + + def test_launcher_switcher_down(self): + """Pressing the down arrow key while switcher is activated must work.""" + self.start_switcher_with_cleanup_cancel() + self.launcher_instance.switcher_down() + # The launcher model has hidden items, so the keynav indexes do not + # increase by 1 each time. This test was failing because the 2nd icon + # had an index of 2, not 1 as expected. The best we can do here is to + # make sure that the index has increased. This opens us to the + # possibility that the launcher really is skipping forward more than one + # icon at a time, but we can't do much about that. + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(0))) + + def test_launcher_switcher_up(self): + """Pressing the up arrow key while switcher is activated must work.""" + self.start_switcher_with_cleanup_cancel() + self.launcher_instance.switcher_up() + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(NotEquals(0))) + + def test_launcher_switcher_next_doesnt_show_shortcuts(self): + """Moving forward in launcher switcher must not show launcher shortcuts.""" + self.start_switcher_with_cleanup_cancel() + self.launcher_instance.switcher_next() + # sleep so that the shortcut timeout could be triggered + sleep(2) + self.assertThat(self.launcher_instance.shortcuts_shown, Eventually(Equals(False))) + + def test_launcher_switcher_prev_doesnt_show_shortcuts(self): + """Moving backward in launcher switcher must not show launcher shortcuts.""" + self.start_switcher_with_cleanup_cancel() + self.launcher_instance.switcher_prev() + # sleep so that the shortcut timeout could be triggered + sleep(2) + self.assertThat(self.launcher_instance.shortcuts_shown, Eventually(Equals(False))) + + def test_launcher_switcher_cycling_forward(self): + """Launcher Switcher must loop through icons when cycling forwards""" + self.start_switcher_with_cleanup_cancel() + prev_icon = 0 + num_icons = self.unity.launcher.model.num_launcher_icons() + logger.info("This launcher has %d icons", num_icons) + for icon in range(1, num_icons): + self.launcher_instance.switcher_next() + # FIXME We can't directly check for selection/icon number equalty + # since the launcher model also contains "hidden" icons that aren't + # shown, so the selection index can increment by more than 1. + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(prev_icon))) + prev_icon = self.unity.launcher.key_nav_selection + + self.launcher_instance.switcher_next() + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(Equals(0))) + + def test_launcher_switcher_cycling_backward(self): + """Launcher Switcher must loop through icons when cycling backwards""" + self.start_switcher_with_cleanup_cancel() + self.launcher_instance.switcher_prev() + # FIXME We can't directly check for self.unity.launcher.num_launcher_icons - 1 + self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(1))) + + def test_launcher_switcher_activate_keep_focus(self): + """Activating a running launcher icon should focus the application.""" + calc = self.process_manager.start_app("Calculator") + mahjongg = self.process_manager.start_app("Mahjongg") + self.assertTrue(mahjongg.is_active) + self.assertFalse(calc.is_active) + + self.start_switcher_with_cleanup_cancel() + + self.launcher_instance.keyboard_select_icon(tooltip_text=calc.name) + self.launcher_instance.switcher_activate() + + self.assertThat(lambda: calc.is_active, Eventually(Equals(True))) + self.assertThat(lambda: mahjongg.is_active, Eventually(Equals(False))) + + def test_launcher_switcher_using_shorcuts(self): + """Using some other shortcut while switcher is active must cancel switcher.""" + self.start_switcher_with_cleanup_cancel() + self.keyboard.press_and_release("s") + sleep(.25) + self.keyboard.press_and_release("Escape") + sleep(.25) + self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_tooltips.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_tooltips.py @@ -0,0 +1,93 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2013 Canonical +# Authors: Jacob Edwards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from autopilot.matchers import Eventually +from testtools.matchers import Equals, NotEquals +from time import sleep + +from unity.tests.launcher import LauncherTestCase, _make_scenarios + +class LauncherTooltipTests(LauncherTestCase): + """Tests whether tooltips display only at appropriate times.""" + + def setUp(self): + super(LauncherTooltipTests, self).setUp() + self.set_unity_option('launcher_hide_mode', 0) + self.launcher_instance.move_mouse_to_right_of_launcher() + self.icons = self.unity.launcher.model.get_launcher_icons(visible_only=True) + + def test_launcher_tooltip_show(self): + """Tests whether icon tooltips delay showing themselves and, + once shown, whether subsequent icons show them instantly.""" + for i in self.icons: + tooltip = i.get_tooltip() + if not tooltip: + continue + self.assertThat(tooltip.active, Eventually(Equals(False))) + + # only reveal tooltips after short wait + self.assertEqual(self.get_reveal_behavior(self.icons[0]), self.DELAYED) + + # must avoid the accordion effect, as the icons start to pass to quickly + size = len(self.icons) + if size > 5: + size = 5 + + # subsequent tooltips reveal instantly, but are destroyed on exit, meaning None + a, b = 0, 1 + while b < size: + self.mouse.move(self.icons[b].center.x, self.icons[b].center.y) + self.assertThat(lambda: self.icons[b].get_tooltip(), Eventually(NotEquals(None))) + self.assertThat(self.icons[b].get_tooltip().active, Eventually(Equals(True))) + self.assertThat(self.icons[a].get_tooltip(), Equals(None)) + a, b = a + 1, b + 1 + b -= 1 + + # leaving launcher clears tooltips, and instant reveal + self.launcher_instance.move_mouse_to_right_of_launcher() + self.assertEqual(self.get_reveal_behavior(self.icons[b]), self.DELAYED) + + def test_launcher_tooltip_disabling(self): + """Tests whether clicking on an icon hides its tooltip.""" + bfb, other = self.icons[0], self.icons[1] + self.assertEqual(self.get_reveal_behavior(bfb), self.DELAYED) + + # clicking icon hides its launcher until further input + self.mouse.click() + self.assertEqual(self.get_reveal_behavior(bfb), self.NEVER) + self.mouse.click() + + # normal behavior resumes on moving away from icon + self.assertEqual(self.get_reveal_behavior(other), self.DELAYED) + self.assertEqual(self.get_reveal_behavior(bfb), self.INSTANT) + + def test_launcher_bfb_tooltip_when_open(self): + """Tests whether hovering over the active BFB starts a tooltip timer""" + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + bfb, other = self.icons[0], self.icons[1] + + # hovering an open dash's BFB does not show a tooltip ... + self.assertEqual(self.get_reveal_behavior(bfb), self.NEVER) + + # ... nor did it timeout instant tooltips for other icons + self.assertEqual(self.get_reveal_behavior(other), self.INSTANT) + + # Tooltip reveal types + (INSTANT, DELAYED, NEVER) = range(3) + + def get_reveal_behavior(self, icon): + self.mouse.move(icon.center.x, icon.center.y) + tooltip = icon.get_tooltip() + if tooltip and tooltip.active: + return self.INSTANT + sleep(1.2) + tooltip = icon.get_tooltip() + if tooltip and tooltip.active: + return self.DELAYED + return self.NEVER only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_visual.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/launcher/test_visual.py @@ -0,0 +1,82 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Authors: Thomi Richards, +# Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +import logging +from testtools.matchers import Equals +from time import sleep + +from unity.emulators.icons import BFBLauncherIcon +from unity.tests.launcher import LauncherTestCase + +logger = logging.getLogger(__name__) + + +class LauncherVisualTests(LauncherTestCase): + """Tests for visual aspects of the launcher (icon saturation etc.).""" + + def test_keynav_from_dash_saturates_icons(self): + """Starting super+tab switcher from the dash must resaturate launcher icons. + + Tests fix for bug #913569. + """ + bfb = self.unity.launcher.model.get_bfb_icon() + self.mouse.move(bfb.center.x, bfb.center.y) + self.unity.dash.ensure_visible() + current_monitor = self.unity.dash.monitor + + sleep(1) + # We can't use 'launcher_instance.switcher_start()' since it moves the mouse. + self.keybinding_hold_part_then_tap("launcher/switcher") + self.addCleanup(self.keybinding_release, "launcher/switcher") + self.addCleanup(self.keybinding, "launcher/switcher/exit") + + self.keybinding_tap("launcher/switcher/next") + for icon in self.unity.launcher.model.get_launcher_icons(): + self.assertFalse(icon.monitors_desaturated[current_monitor]) + + def test_opening_dash_desaturates_icons(self): + """Opening the dash must desaturate all the launcher icons.""" + self.unity.dash.ensure_visible() + current_monitor = self.unity.dash.monitor + + self.addCleanup(self.unity.dash.ensure_hidden) + + for icon in self.unity.launcher.model.get_launcher_icons(): + if isinstance(icon, BFBLauncherIcon): + self.assertFalse(icon.monitors_desaturated[current_monitor]) + else: + self.assertTrue(icon.monitors_desaturated[current_monitor]) + + def test_opening_dash_with_mouse_over_launcher_keeps_icon_saturation(self): + """Opening dash with mouse over launcher must not desaturate icons.""" + launcher_instance = self.get_launcher() + x,y,w,h = launcher_instance.geometry + self.mouse.move(x + w/2, y + h/2) + sleep(.5) + self.unity.dash.ensure_visible() + current_monitor = self.unity.dash.monitor + + self.addCleanup(self.unity.dash.ensure_hidden) + for icon in self.unity.launcher.model.get_launcher_icons(): + self.assertFalse(icon.monitors_desaturated[current_monitor]) + + def test_mouse_over_with_dash_open_desaturates_icons(self): + """Moving mouse over launcher with dash open must saturate icons.""" + self.unity.dash.ensure_visible() + current_monitor = self.unity.dash.monitor + + self.addCleanup(self.unity.dash.ensure_hidden) + sleep(.5) + self.mouse.move_to_object(self.get_launcher()) + sleep(.5) + for icon in self.unity.launcher.model.get_launcher_icons(): + self.assertFalse(icon.monitors_desaturated[current_monitor]) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_command_lens.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_command_lens.py @@ -0,0 +1,96 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +from testtools.matchers import Equals, NotEquals, GreaterThan, MatchesPredicate +from time import sleep + +from unity.tests import UnityTestCase + +import gettext + +class CommandScopeSearchTests(UnityTestCase): + """Test the command scope search bahavior.""" + + def setUp(self): + super(CommandScopeSearchTests, self).setUp() + gettext.install("unity-scope-applications") + + def tearDown(self): + self.unity.dash.ensure_hidden() + super(CommandScopeSearchTests, self).tearDown() + + def wait_for_category(self, scope, group): + """Method to wait for a specific category""" + get_scope_fn = lambda: scope.get_category_by_name(group) + self.assertThat(get_scope_fn, Eventually(NotEquals(None), timeout=20)) + return get_scope_fn() + + def test_no_results(self): + """An empty string should get no results.""" + self.unity.dash.reveal_command_scope() + command_scope = self.unity.dash.get_current_scope() + + if self.unity.dash.search_string != "": + self.keyboard.press_and_release("Delete") + + self.assertThat(self.unity.dash.search_string, Eventually(Equals(""))) + + results_category = self.wait_for_category(command_scope, _("Results")) + self.assertThat(results_category.is_visible, Eventually(Equals(False))) + + def test_results_category_appears(self): + """Results category must appear when there are some results.""" + self.unity.dash.reveal_command_scope() + command_scope = self.unity.dash.get_current_scope() + # lots of apps start with 'a'... + self.keyboard.type("a") + self.assertThat(self.unity.dash.search_string, Eventually(Equals("a"))) + + results_category = self.wait_for_category(command_scope, _("Results")) + self.assertThat(results_category.is_visible, Eventually(Equals(True))) + + def test_result_category_actually_contains_results(self): + """With a search string of 'a', the results category must contain some results.""" + self.unity.dash.reveal_command_scope() + command_scope = self.unity.dash.get_current_scope() + # lots of apps start with 'a'... + self.keyboard.type("a") + self.assertThat(self.unity.dash.search_string, Eventually(Equals("a"))) + + results_category = self.wait_for_category(command_scope, _("Results")) + self.assertThat(lambda: len(results_category.get_results()), Eventually(GreaterThan(0), timeout=20)) + + def test_run_before_refresh(self): + """Hitting enter before view has updated results must run the correct command.""" + if self.process_manager.app_is_running("Text Editor"): + self.process_manager.close_all_app("Text Editor") + sleep(1) + + self.unity.dash.reveal_command_scope() + self.keyboard.type("g") + sleep(1) + self.keyboard.type("edit", 0.1) + self.keyboard.press_and_release("Enter", 0.1) + self.addCleanup(self.process_manager.close_all_app, "Text Editor") + app_found = self.process_manager.wait_until_application_is_running("gedit.desktop", 5) + self.assertTrue(app_found) + + def test_ctrl_tab_switching(self): + """Pressing Ctrl+Tab after launching command scope must switch to Home scope.""" + self.unity.dash.reveal_command_scope() + self.keybinding("dash/lens/next") + self.assertThat(self.unity.dash.active_scope, Eventually(Equals("home.scope"))) + + def test_ctrl_shift_tab_switching(self): + """Pressing Ctrl+Shift+Tab after launching command scope must switch to Photos or Social scope (Social can be hidden by default).""" + self.unity.dash.reveal_command_scope() + self.keybinding("dash/lens/prev") + self.assertThat(self.unity.dash.active_scope, Eventually(MatchesPredicate(lambda x: x in ["photos.scope", "social.scope"], '%s is not last scope'))) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_dash.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_dash.py @@ -0,0 +1,1262 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2010 Canonical +# Author: Alex Launi +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.clipboard import get_clipboard_contents +from autopilot.display import move_mouse_to_screen +from autopilot.matchers import Eventually +from testtools.matchers import Equals, NotEquals, GreaterThan +from time import sleep +from tempfile import mkstemp +from os import remove +from subprocess import call + +from unity.tests import UnityTestCase + +import gettext + +class DashTestCase(UnityTestCase): + def setUp(self): + super(DashTestCase, self).setUp() + self.set_unity_log_level("unity.shell.compiz", "DEBUG") + self.set_unity_log_level("unity.launcher", "DEBUG") + self.unity.dash.ensure_hidden() + # On shutdown, ensure hidden too. Also add a delay. Cleanup is LIFO. + self.addCleanup(self.unity.dash.ensure_hidden) + self.addCleanup(sleep, 1) + + def get_current_preview(self): + """Method to open the currently selected preview, if opened.""" + preview_fn = lambda: self.preview_container.current_preview + self.assertThat(preview_fn, Eventually(NotEquals(None))) + self.assertThat(self.unity.dash.preview_animation, Eventually(Equals(1.0))) + self.assertThat(self.preview_container.animating, Eventually(Equals(False))) + + return preview_fn() + + def wait_for_category(self, scope, group): + """Method to wait for a specific category""" + get_scope_fn = lambda: scope.get_category_by_name(group) + self.assertThat(get_scope_fn, Eventually(NotEquals(None), timeout=20)) + return get_scope_fn() + + def wait_for_result_settle(self): + """wait for row count to settle""" + old_row_count = -1 + new_row_count = self.unity.dash.get_num_rows() + while(old_row_count != new_row_count): + sleep(1) + old_row_count = new_row_count + new_row_count = self.unity.dash.get_num_rows() + + +class DashRevealTests(DashTestCase): + """Test the Unity dash Reveal.""" + + def test_dash_reveal(self): + """Ensure we can show and hide the dash.""" + self.unity.dash.ensure_visible() + self.unity.dash.ensure_hidden() + + def test_application_scope_shortcut(self): + """Application scope must reveal when Super+a is pressed.""" + self.unity.dash.reveal_application_scope() + self.assertThat(self.unity.dash.active_scope, Eventually(Equals('applications.scope'))) + + def test_music_scope_shortcut(self): + """Music scope must reveal when Super+w is pressed.""" + self.unity.dash.reveal_music_scope() + self.assertThat(self.unity.dash.active_scope, Eventually(Equals('music.scope'))) + + def test_file_scope_shortcut(self): + """File scope must reveal when Super+f is pressed.""" + self.unity.dash.reveal_file_scope() + self.assertThat(self.unity.dash.active_scope, Eventually(Equals('files.scope'))) + + def test_video_scope_shortcut(self): + """Video scope must reveal when super+v is pressed.""" + self.unity.dash.reveal_video_scope() + self.assertThat(self.unity.dash.active_scope, Eventually(Equals('video.scope'))) + + def test_command_scope_shortcut(self): + """Run Command scope must reveat on alt+F2.""" + self.unity.dash.reveal_command_scope() + self.assertThat(self.unity.dash.active_scope, Eventually(Equals('commands.scope'))) + + def test_can_go_from_dash_to_command_scope(self): + """Switch to command scope without closing the dash.""" + self.unity.dash.ensure_visible() + self.unity.dash.reveal_command_scope() + self.assertThat(self.unity.dash.active_scope, Eventually(Equals('commands.scope'))) + + def test_command_lens_can_close_itself(self): + """We must be able to close the Command lens with Alt+F2""" + self.unity.dash.reveal_command_scope() + self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) + + self.keybinding("lens_reveal/command") + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + + def test_alt_f4_close_dash(self): + """Dash must close on alt+F4.""" + self.unity.dash.ensure_visible() + self.keyboard.press_and_release("Alt+F4") + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + + def test_alt_f4_close_dash_with_capslock_on(self): + """Dash must close on Alt+F4 even when the capslock is turned on.""" + self.keyboard.press_and_release("Caps_Lock") + self.addCleanup(self.keyboard.press_and_release, "Caps_Lock") + + self.unity.dash.ensure_visible() + self.keyboard.press_and_release("Alt+F4") + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + + def test_closes_mouse_down_outside(self): + """Test that a mouse down outside of the dash closes the dash.""" + if (self.unity.dash.view.form_factor != "desktop"): + self.skip("Not in desktop form-factor.") + + self.unity.dash.ensure_visible() + current_monitor = self.unity.dash.monitor + + (x,y,w,h) = self.unity.dash.geometry + (screen_x,screen_y,screen_w,screen_h) = self.display.get_screen_geometry(current_monitor) + + self.mouse.move(x + w + (screen_w-((screen_x-x)+w))/2, y + h + (screen_h-((screen_y-y)+h))/2) + self.mouse.click() + + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + + def test_closes_then_focuses_window_on_mouse_down(self): + """If 2 windows are open with 1 maximized and the non-maxmized + focused. Then from the Dash clicking on the maximized window + must focus that window and close the dash. + """ + if (self.unity.dash.view.form_factor != "desktop"): + self.skip("Not in desktop form-factor.") + + char_win = self.process_manager.start_app("Character Map") + self.keybinding("window/maximize") + calc_win = self.process_manager.start_app("Calculator") + + self.unity.dash.ensure_visible() + + # Click bottom right of the screen, but take into account the non-maximized window - + # we do not want to click on it as it focuses the wrong window + w = self.display.get_screen_width() - 1 + h = self.display.get_screen_height() - 1 + + # If the mouse is over the non-maximized window, move it away from it. + (calc_x, calc_y, calc_w, calc_h) = calc_win.get_windows()[0].geometry + if calc_x <= w <= calc_x+calc_w: + grab_padding = 15 + w = w - (calc_w + grab_padding) + + self.mouse.move(w,h) + self.mouse.click() + + self.assertProperty(char_win, is_active=True) + + def test_dash_opens_when_fullscreen_window(self): + """ The Dash must not open if a window is fullscreen. """ + gedit = self.process_manager.start_app("Text Editor") + self.keyboard.press_and_release('F11') + + monitor = gedit.get_windows()[0].monitor + move_mouse_to_screen(monitor) + + self.keybinding("dash/reveal") + self.addCleanup(self.unity.dash.ensure_hidden) + + self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) + + +class DashRevealWithSpreadTests(DashTestCase): + """Test the interaction of the Dash with the Spead/Scale + + The Spread (or Scale) in Quantal is not activated if there is no active + apps. We use a place holder app so that it is activated as we require. + + """ + + def start_placeholder_app(self): + window_spec = { + "Title": "Placeholder application", + } + self.launch_test_window(window_spec) + + def test_dash_closes_on_spread(self): + """This test shows that when the spread is initiated, the dash closes.""" + self.start_placeholder_app() + self.unity.dash.ensure_visible() + self.addCleanup(self.keybinding, "spread/cancel") + self.keybinding("spread/start") + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + + def test_dash_opens_when_in_spread(self): + """This test shows the dash opens when in spread mode.""" + self.start_placeholder_app() + self.keybinding("spread/start") + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) + + self.unity.dash.ensure_visible() + self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) + + def test_command_scope_opens_when_in_spread(self): + """This test shows the command scope opens when in spread mode.""" + self.start_placeholder_app() + self.keybinding("spread/start") + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) + + self.unity.dash.reveal_command_scope() + self.assertThat(self.unity.dash.active_scope, Eventually(Equals('commands.scope'))) + + def test_scope_opens_when_in_spread(self): + """This test shows that any scope opens when in spread mode.""" + self.start_placeholder_app() + self.keybinding("spread/start") + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) + + self.unity.dash.reveal_application_scope() + self.assertThat(self.unity.dash.active_scope, Eventually(Equals('applications.scope'))) + + +class DashSearchInputTests(DashTestCase): + """Test features involving input to the dash search""" + + def assertSearchText(self, text): + self.assertThat(self.unity.dash.search_string, Eventually(Equals(text))) + + def test_search_keyboard_focus(self): + """Dash must put keyboard focus on the search bar at all times.""" + self.unity.dash.ensure_visible() + self.keyboard.type("Hello") + self.assertSearchText("Hello") + + +class DashMultiKeyTests(DashSearchInputTests): + def setUp(self): + # set the multi key first so that we're not getting a new _DISPLAY while keys are held down. + old_value = self.call_gsettings_cmd('get', 'org.gnome.libgnomekbd.keyboard', 'options') + self.addCleanup(self.call_gsettings_cmd, 'set', 'org.gnome.libgnomekbd.keyboard', 'options', old_value) + self.call_gsettings_cmd('set', 'org.gnome.libgnomekbd.keyboard', 'options', "['Compose key\tcompose:caps']") + + # FIXME Setting this to just get past blocking... implmenting a better fix ASAP + call(["setxkbmap", "-option", "compose:rwin"]) + + super(DashMultiKeyTests, self).setUp() + + def test_multi_key(self): + """Pressing 'Multi_key' must not add any characters to the search.""" + self.unity.dash.reveal_application_scope() + self.keyboard.press_and_release('Multi_key') + self.keyboard.type("o") + self.assertSearchText("") + + def test_multi_key_o(self): + """Pressing the sequences 'Multi_key' + '^' + 'o' must produce 'ô'.""" + self.unity.dash.reveal_application_scope() + self.keyboard.press_and_release('Multi_key') + self.keyboard.type("^o") + self.assertSearchText(u'\xf4') + + def test_multi_key_copyright(self): + """Pressing the sequences 'Multi_key' + 'c' + 'o' must produce '©'.""" + self.unity.dash.reveal_application_scope() + self.keyboard.press_and_release('Multi_key') + self.keyboard.type("oc") + self.assertSearchText(u'\xa9') + + def test_multi_key_delete(self): + """Pressing 'Multi_key' must not get stuck looking for a sequence.""" + self.unity.dash.reveal_application_scope() + self.keyboard.type("dd") + self.keyboard.press_and_release('Multi_key') + self.keyboard.press_and_release('BackSpace') + self.keyboard.press_and_release('BackSpace') + self.assertSearchText("d") + + +class DashKeyNavTests(DashTestCase): + """Test the unity Dash keyboard navigation.""" + + def test_scopebar_gets_keyfocus(self): + """Test that the scopebar gets key focus after using Down keypresses.""" + self.unity.dash.ensure_visible() + self.wait_for_result_settle() + + # Make sure that the scope bar can get the focus + for i in range(self.unity.dash.get_num_rows()): + self.keyboard.press_and_release("Down") + scopebar = self.unity.dash.view.get_scopebar() + self.assertThat(scopebar.focused_scope_icon, Eventually(NotEquals(''))) + + def test_scopebar_focus_changes(self): + """Scopebar focused icon should change with Left and Right keypresses.""" + self.unity.dash.ensure_visible() + self.wait_for_result_settle() + + for i in range(self.unity.dash.get_num_rows()): + self.keyboard.press_and_release("Down") + scopebar = self.unity.dash.view.get_scopebar() + + current_focused_icon = scopebar.focused_scope_icon + + self.keyboard.press_and_release("Right") + self.assertThat(scopebar.focused_scope_icon, Eventually(NotEquals(current_focused_icon))) + + self.keyboard.press_and_release("Left") + self.assertThat(scopebar.focused_scope_icon, Eventually(Equals(current_focused_icon))) + + def test_scopebar_enter_activation(self): + """Must be able to activate ScopeBar icons that have focus with an Enter keypress.""" + self.unity.dash.ensure_visible() + self.wait_for_result_settle() + + for i in range(self.unity.dash.get_num_rows()): + self.keyboard.press_and_release("Down") + self.keyboard.press_and_release("Right") + scopebar = self.unity.dash.view.get_scopebar() + focused_icon = scopebar.focused_scope_icon + self.keyboard.press_and_release("Enter") + + self.assertThat(scopebar.active_scope, Eventually(Equals(focused_icon))) + + # scopebar should lose focus after activation. + self.assertFalse(hasattr(scopebar, 'focused_scope_icon')) + + def test_focus_returns_to_searchbar(self): + """This test makes sure that the focus is returned to the searchbar of the newly + activated scope.""" + self.unity.dash.ensure_visible() + self.wait_for_result_settle() + + for i in range(self.unity.dash.get_num_rows()): + self.keyboard.press_and_release("Down") + self.keyboard.press_and_release("Right") + scopebar = self.unity.dash.view.get_scopebar() + focused_icon = scopebar.focused_scope_icon + self.keyboard.press_and_release("Enter") + + self.assertThat(scopebar.active_scope, Eventually(Equals(focused_icon))) + self.assertFalse(hasattr(scopebar, 'focused_scope_icon')) + + # Now we make sure if the newly activated scope searchbar have the focus. + self.keyboard.type("HasFocus") + + self.assertThat(self.unity.dash.search_string, Eventually(Equals("HasFocus"))) + + def test_category_header_keynav(self): + """ Tests that a category header gets focus when 'down' is pressed after the + dash is opened + + OK important to note that this test only tests that A category is + focused, not the first and from doing this it seems that it's common + for a header other than the first to get focus. + """ + self.unity.dash.ensure_visible() + self.wait_for_result_settle() + # Make sure that a category have the focus. + self.keyboard.press_and_release("Down") + scope = self.unity.dash.get_current_scope() + category = scope.get_focused_category() + self.assertIsNot(category, None) + # Make sure that the category is highlighted. + self.assertTrue(category.header_is_highlighted) + + def test_control_tab_scope_cycle(self): + """This test makes sure that Ctrl+Tab cycles scopes.""" + self.unity.dash.ensure_visible() + self.wait_for_result_settle() + + self.keyboard.press('Control') + self.keyboard.press_and_release('Tab') + self.keyboard.release('Control') + + scopebar = self.unity.dash.view.get_scopebar() + self.assertEqual(scopebar.active_scope, u'applications.scope') + + self.keyboard.press('Control') + self.keyboard.press('Shift') + self.keyboard.press_and_release('Tab') + self.keyboard.release('Control') + self.keyboard.release('Shift') + + self.assertThat(scopebar.active_scope, Eventually(Equals('home.scope'))) + + def test_tab_cycle_category_headers(self): + """ Makes sure that pressing tab cycles through the category headers""" + self.unity.dash.ensure_visible() + self.wait_for_result_settle() + scope = self.unity.dash.get_current_scope() + + # Test that tab cycles through the categories. + # + 1 is the filter bar + for category in scope.get_categories(only_visible=True): + self.keyboard.press_and_release('Tab') + selected = scope.get_focused_category() + expected = category if category.expand_label_is_visible else None + self.assertEqual(selected, expected) + + def test_tab_with_filter_bar(self): + """ This test makes sure that Tab works well with the filter bara.""" + self.unity.dash.reveal_application_scope() + self.wait_for_result_settle() + scope = self.unity.dash.get_current_scope() + + # Tabs to last category + for i in range(scope.get_num_visible_categories()): + self.keyboard.press_and_release('Tab') + + self.keyboard.press_and_release('Tab') + self.assertThat(self.unity.dash.searchbar.expander_has_focus, Eventually(Equals(True))) + + filter_bar = scope.get_filterbar() + if not self.unity.dash.searchbar.showing_filters: + self.keyboard.press_and_release('Enter') + self.assertThat(self.unity.dash.searchbar.showing_filters, Eventually(Equals(True))) + self.addCleanup(filter_bar.ensure_collapsed) + + for i in range(filter_bar.get_num_filters()): + self.keyboard.press_and_release('Tab') + new_focused_filter = filter_bar.get_focused_filter() + self.assertIsNotNone(new_focused_filter) + + # Ensure that tab cycles back to a category header + self.keyboard.press_and_release('Tab') + category = scope.get_focused_category() + self.assertIsNot(category, None) + + def test_bottom_up_keynav_with_filter_bar(self): + """This test makes sure that bottom-up key navigation works well + in the dash filter bar. + """ + self.unity.dash.reveal_application_scope() + self.wait_for_result_settle() + scope = self.unity.dash.get_current_scope() + + filter_bar = scope.get_filterbar() + # Need to ensure the filter expander has focus, so if it's already + # expanded, we collapse it first: + filter_bar.ensure_collapsed() + filter_bar.ensure_expanded() + + # Tab to fist filter expander + self.keyboard.press_and_release('Tab') + self.assertThat(filter_bar.get_focused_filter, Eventually(NotEquals(None))) + old_focused_filter = filter_bar.get_focused_filter() + old_focused_filter.ensure_expanded() + + # Tab to the next filter expander + self.keyboard.press_and_release('Tab') + self.assertThat(filter_bar.get_focused_filter, Eventually(NotEquals(None))) + new_focused_filter = filter_bar.get_focused_filter() + self.assertNotEqual(old_focused_filter, new_focused_filter) + new_focused_filter.ensure_expanded() + + # Move the focus up. + self.keyboard.press_and_release("Up") + self.assertThat(filter_bar.get_focused_filter, Eventually(Equals(None))) + self.assertThat(old_focused_filter.content_has_focus, Eventually(Equals(True))) + + +class DashClipboardTests(DashTestCase): + """Test the Unity clipboard""" + + def test_ctrl_a(self): + """ This test if ctrl+a selects all text """ + self.unity.dash.ensure_visible() + + self.keyboard.type("SelectAll") + self.assertThat(self.unity.dash.search_string, Eventually(Equals("SelectAll"))) + + self.keyboard.press_and_release("Ctrl+a") + self.keyboard.press_and_release("Delete") + self.assertThat(self.unity.dash.search_string, Eventually(Equals(''))) + + def test_ctrl_c(self): + """ This test if ctrl+c copies text into the clipboard """ + self.unity.dash.ensure_visible() + + self.keyboard.type("Copy") + self.assertThat(self.unity.dash.search_string, Eventually(Equals("Copy"))) + + self.keyboard.press_and_release("Ctrl+a") + self.keyboard.press_and_release("Ctrl+c") + + self.assertThat(get_clipboard_contents, Eventually(Equals("Copy"))) + + def test_ctrl_x(self): + """ This test if ctrl+x deletes all text and copys it """ + self.unity.dash.ensure_visible() + + self.keyboard.type("Cut") + self.assertThat(self.unity.dash.search_string, Eventually(Equals("Cut"))) + + self.keyboard.press_and_release("Ctrl+a") + self.keyboard.press_and_release("Ctrl+x") + self.assertThat(self.unity.dash.search_string, Eventually(Equals(""))) + + self.assertThat(get_clipboard_contents, Eventually(Equals('Cut'))) + + def test_ctrl_c_v(self): + """ This test if ctrl+c and ctrl+v copies and pastes text""" + self.unity.dash.ensure_visible() + + self.keyboard.type("CopyPaste") + self.assertThat(self.unity.dash.search_string, Eventually(Equals("CopyPaste"))) + + self.keyboard.press_and_release("Ctrl+a") + self.keyboard.press_and_release("Ctrl+c") + self.keyboard.press_and_release("Ctrl+v") + self.keyboard.press_and_release("Ctrl+v") + + self.assertThat(self.unity.dash.search_string, Eventually(Equals('CopyPasteCopyPaste'))) + + def test_ctrl_x_v(self): + """ This test if ctrl+x and ctrl+v cuts and pastes text""" + self.unity.dash.ensure_visible() + + self.keyboard.type("CutPaste") + self.assertThat(self.unity.dash.search_string, Eventually(Equals("CutPaste"))) + + self.keyboard.press_and_release("Ctrl+a") + self.keyboard.press_and_release("Ctrl+x") + self.keyboard.press_and_release("Ctrl+v") + self.keyboard.press_and_release("Ctrl+v") + + self.assertThat(self.unity.dash.search_string, Eventually(Equals('CutPasteCutPaste'))) + + def test_middle_click_paste(self): + """Tests if Middle mouse button pastes into searchbar""" + + self.process_manager.start_app_window("Calculator", locale='C') + + self.keyboard.type("ThirdButtonPaste") + self.keyboard.press_and_release("Ctrl+a") + + self.unity.dash.ensure_visible() + + self.mouse.click_object(self.unity.dash.searchbar, button=2) + + self.assertThat(self.unity.dash.search_string, Eventually(Equals('ThirdButtonPaste'))) + + +class DashKeyboardFocusTests(DashTestCase): + """Tests that keyboard focus works.""" + + def assertSearchText(self, text): + self.assertThat(self.unity.dash.search_string, Eventually(Equals(text))) + + def test_filterbar_expansion_leaves_kb_focus(self): + """Expanding or collapsing the filterbar must keave keyboard focus in the + search bar. + """ + self.unity.dash.reveal_application_scope() + filter_bar = self.unity.dash.get_current_scope().get_filterbar() + filter_bar.ensure_collapsed() + + self.keyboard.type("hello") + filter_bar.ensure_expanded() + self.addCleanup(filter_bar.ensure_collapsed) + self.keyboard.type(" world") + self.assertSearchText("hello world") + + def test_keep_focus_on_application_opens(self): + """The Dash must keep key focus as well as stay open if an app gets opened from an external source. """ + + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + self.process_manager.start_app_window("Calculator") + sleep(1) + + self.keyboard.type("HasFocus") + self.assertSearchText("HasFocus") + + +class DashScopeResultsTests(DashTestCase): + """Tests results from the scope view.""" + + def test_results_message_empty_search(self): + """This tests a message is not shown when there is no text.""" + self.unity.dash.reveal_application_scope() + scope = self.unity.dash.get_current_scope() + self.assertThat(scope.no_results_active, Eventually(Equals(False))) + + def test_results_message(self): + """This test no mesage will be shown when results are there.""" + self.unity.dash.reveal_application_scope() + self.keyboard.type("Terminal") + self.assertThat(self.unity.dash.search_string, Eventually(Equals("Terminal"))) + scope = self.unity.dash.get_current_scope() + self.assertThat(scope.no_results_active, Eventually(Equals(False))) + + def test_no_results_message(self): + """This test shows a message will appear in the scope.""" + self.unity.dash.reveal_application_scope() + self.keyboard.type("qwerlkjzvxc") + self.assertThat(self.unity.dash.search_string, Eventually(Equals("qwerlkjzvxc"))) + scope = self.unity.dash.get_current_scope() + self.assertThat(scope.no_results_active, Eventually(Equals(True))) + + def test_results_update_on_filter_changed(self): + """This test makes sure the results change when filters change.""" + gettext.install("unity-scope-applications") + self.unity.dash.reveal_application_scope() + scope = self.unity.dash.get_current_scope() + self.keyboard.type(" ") + self.assertThat(self.unity.dash.search_string, Eventually(Equals(" "))) + + # wait for "Installed" category + old_results_category = self.wait_for_category(scope, _("Installed")) + + self.assertThat(lambda: len(old_results_category.get_results()), Eventually(GreaterThan(0), timeout=20)) + old_results = old_results_category.get_results() + + # FIXME: This should be a method on the dash emulator perhaps, or + # maybe a proper method of this class. It should NOT be an inline + # function that is only called once! + def activate_filter(add_cleanup = False): + # Tabs to last category + for i in range(scope.get_num_visible_categories()): + self.keyboard.press_and_release('Tab') + + self.keyboard.press_and_release('Tab') + self.assertThat(self.unity.dash.searchbar.expander_has_focus, Eventually(Equals(True))) + + filter_bar = scope.get_filterbar() + if not self.unity.dash.searchbar.showing_filters: + self.keyboard.press_and_release('Enter') + self.assertThat(self.unity.dash.searchbar.showing_filters, Eventually(Equals(True))) + if add_cleanup: + self.addCleanup(filter_bar.ensure_collapsed) + + # Tab to the "Type" filter in apps scope + self.keyboard.press_and_release('Tab') + new_focused_filter = filter_bar.get_focused_filter() + self.assertIsNotNone(new_focused_filter) + + self.keyboard.press_and_release("Down") + self.keyboard.press_and_release("Down") + # We should be on the Customization category + self.keyboard.press_and_release('Enter') + sleep(2) + + activate_filter(True) + self.addCleanup(activate_filter) + + results_category = self.wait_for_category(scope, _("Installed")) + self.assertThat(lambda: len(results_category.get_results()), Eventually(GreaterThan(0), timeout=20)) + + results = results_category.get_results() + self.assertIsNot(results, old_results) + + # so we can clean up properly + self.keyboard.press_and_release('BackSpace') + + +class DashVisualTests(DashTestCase): + """Tests that the dash visual is correct.""" + + def test_closing_dash_hides_current_scope(self): + """When exiting from the dash the current scope must set it self to not visible.""" + + self.unity.dash.ensure_visible() + scope = self.unity.dash.get_current_scope() + self.unity.dash.ensure_hidden() + + self.assertThat(scope.visible, Eventually(Equals(False))) + + def test_dash_position_with_non_default_launcher_width(self): + """There should be no empty space between launcher and dash when the launcher + has a non-default width. + + """ + monitor = self.unity.dash.monitor + launcher = self.unity.launcher.get_launcher_for_monitor(monitor) + + self.set_unity_option('icon_size', 60) + self.assertThat(launcher.icon_size, Eventually(Equals(66))) + + self.unity.dash.ensure_visible() + + self.assertThat(self.unity.dash.view.x, Eventually(Equals(launcher.geometry.x + launcher.geometry.width - 1))) + + + def test_see_more_result_alignment(self): + """The see more results label should be baseline aligned + with the category name label. + """ + self.unity.dash.reveal_application_scope() + + scope = self.unity.dash.get_current_scope() + self.assertThat(lambda: len(scope.get_categories()), Eventually(GreaterThan(0), timeout=20)) + + for group in scope.get_categories(): + if (group.is_visible and group.expand_label_is_visible): + expand_label_y = group.expand_label_y + group.expand_label_baseline + name_label_y = group.name_label_y + group.name_label_baseline + self.assertThat(expand_label_y, Equals(name_label_y)) + + +class DashScopeBarTests(DashTestCase): + """Tests that the scopebar works well.""" + + def setUp(self): + super(DashScopeBarTests, self).setUp() + self.unity.dash.ensure_visible() + self.scopebar = self.unity.dash.view.get_scopebar() + + def test_click_inside_highlight(self): + """Scope selection should work when clicking in + the rectangle outside of the icon. + """ + app_icon = self.scopebar.get_icon_by_name(u'applications.scope') + self.mouse.click_object(app_icon) + + self.assertThat(self.scopebar.active_scope, Eventually(Equals('applications.scope'))) + + +class DashBorderTests(DashTestCase): + """Tests that the dash border works well. + """ + def setUp(self): + super(DashBorderTests, self).setUp() + self.unity.dash.ensure_visible() + + def test_click_right_border(self): + """Clicking on the right dash border should do nothing, + *NOT* close the dash. + """ + if (self.unity.dash.view.form_factor != "desktop"): + self.skip("Not in desktop form-factor.") + + x = self.unity.dash.view.x + self.unity.dash.view.width + self.unity.dash.view.right_border_width / 2 + y = self.unity.dash.view.y + self.unity.dash.view.height / 2 + + self.mouse.move(x, y) + self.mouse.click() + + self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) + + def test_click_bottom_border(self): + """Clicking on the bottom dash border should do nothing, + *NOT* close the dash. + """ + if (self.unity.dash.view.form_factor != "desktop"): + self.skip("Not in desktop form-factor.") + + x = self.unity.dash.view.x + self.unity.dash.view.width / 2 + y = self.unity.dash.view.y + self.unity.dash.view.height + self.unity.dash.view.bottom_border_height / 2 + + self.mouse.move(x, y) + self.mouse.click() + + self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) + + +class CategoryHeaderTests(DashTestCase): + """Tests that category headers work. + """ + def test_click_inside_highlight(self): + """Clicking into a category highlight must expand/collapse + the view. + """ + gettext.install("unity-scope-applications", unicode=True) + scope = self.unity.dash.reveal_application_scope() + self.addCleanup(self.unity.dash.ensure_hidden) + + # wait for "Installed" category + category = self.wait_for_category(scope, _("Installed")) + + is_expanded = category.is_expanded + + self.mouse.move(self.unity.dash.view.x + self.unity.dash.view.width / 2, + category.header_y + category.header_height / 2) + + self.mouse.click() + self.assertThat(category.is_expanded, Eventually(Equals(not is_expanded))) + + self.mouse.click() + self.assertThat(category.is_expanded, Eventually(Equals(is_expanded))) + + +class PreviewInvocationTests(DashTestCase): + """Tests that dash previews can be opened and closed in different + scopes. + """ + def test_app_scope_preview_open_close(self): + """Right-clicking on an application scope result must show + its preview. + + """ + gettext.install("unity-scope-applications", unicode=True) + scope = self.unity.dash.reveal_application_scope() + self.addCleanup(self.unity.dash.ensure_hidden) + + self.keyboard.type("Software Updater") + + # wait for "Installed" category + category = self.wait_for_category(scope, _("Installed")) + + # wait for some results + self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) + results = category.get_results() + + result = results[0] + # result.preview handles finding xy co-ords and right mouse-click + result.preview(button=2) + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) + + self.keyboard.press_and_release("Escape") + + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_app_scope_lmb_installed_app(self): + """Left-clicking on an application scope result in 'Installed' category + must activate it. + """ + gettext.install("unity-scope-applications", unicode=True) + scope = self.unity.dash.reveal_application_scope() + + self.addCleanup(self.process_manager.close_all_app, "Character Map") + self.addCleanup(self.unity.dash.ensure_hidden) + + self.keyboard.type("Character") + # wait for "Installed" category + category = self.wait_for_category(scope, _("Installed")) + + # wait for some results + self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) + results = category.get_results() + + result = results[0] + # result.preview handles finding xy co-ords and right mouse-click + result.activate(double_click=False) + + # make sure it started + self.assertThat(lambda: len(self.process_manager.get_open_windows_by_application("Character Map")), Eventually(Equals(1), timeout=10)) + + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_home_scope_lmb_app(self): + """Left-clicking on an application scope result in 'Application' category of Home + must activate it. + """ + gettext.install("unity-scope-applications", unicode=True) + self.unity.dash.ensure_visible() + scope = self.unity.dash.view.get_scopeview_by_name("home.scope") + + self.addCleanup(self.process_manager.close_all_app, "Character Map") + self.addCleanup(self.unity.dash.ensure_hidden) + + self.keyboard.type("Character Map") + # wait for "Applications" category + category = self.wait_for_category(scope, _("Applications")) + + # wait for some results + self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) + results = category.get_results() + + result = results[0] + # result.preview handles finding xy co-ords and right mouse-click + result.activate(double_click=False) + + # make sure it started + self.assertThat(lambda: len(self.process_manager.get_open_windows_by_application("Character Map")), Eventually(Equals(1), timeout=10)) + + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_files_scope_preview_open_close(self): + """Right-clicking on a files scope result must show its + preview. + """ + gettext.install("unity-scope-files", unicode=True) + + # Instead of skipping the test, here we can create a dummy file to open and + # make sure the scope result is non-empty + (file_handle, file_path) = mkstemp() + self.addCleanup(remove, file_path) + gedit_win = self.process_manager.start_app_window('Text Editor', files=[file_path], locale='C') + self.assertProperty(gedit_win, is_focused=True) + + # immediately close gedit since we're only interested in getting ZG event when opening temp file. + # this way we avoid an issue with gedit popup dialog when temporary file gets removed by AP + # before closing gedit by AP cleanup code. + gedit_win.close() + + scope = self.unity.dash.reveal_file_scope() + self.addCleanup(self.unity.dash.ensure_hidden) + + # wait for "Recent" category + category = self.wait_for_category(scope, _("Recent")) + + # wait for some results + self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) + results = category.get_results() + + result = results[0] + # result.preview handles finding xy co-ords and right mouse-click + result.preview() + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) + + self.keyboard.press_and_release("Escape") + + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_music_scope_preview_open_close(self): + """Right-clicking on a music scope result must show its + preview. + """ + scope = self.unity.dash.reveal_music_scope() + self.addCleanup(self.unity.dash.ensure_hidden) + + category = scope.get_category_by_name("Songs") + # Incase there was no music ever played we skip the test instead + # of failing. + if category is None or not category.is_visible: + self.skipTest("This scope is probably empty") + + # wait for some results + self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) + results = category.get_results() + + result = results[0] + # result.preview handles finding xy co-ords and right mouse-click + result.preview() + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) + + self.keyboard.press_and_release("Escape") + + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_video_scope_preview_open_close(self): + """Right-clicking on a video scope result must show its + preview. + """ + gettext.install("unity-scope-video", unicode=True) + + def get_category(scope): + category = scope.get_category_by_name(_("Recently Viewed")) + # If there was no video played on this system this category is expected + # to be empty, if its empty we check if the 'Online' category have any + # contents, if not then we skip the test. + if category is None or not category.is_visible: + category = scope.get_category_by_name("Online") + if category is None or not category.is_visible: + self.skipTest("This scope is probably empty") + return category + + scope = self.unity.dash.reveal_video_scope() + self.addCleanup(self.unity.dash.ensure_hidden) + + # get category. might not be any. + category = get_category(scope) + + # wait for some results + self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) + results = category.get_results() + + result = results[0] + # result.preview handles finding xy co-ords and right mouse-click + result.preview() + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) + + self.keyboard.press_and_release("Escape") + + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_preview_key(self): + """Pressing menu key on a selected dash result must show + its preview. + """ + gettext.install("unity-scope-applications", unicode=True) + scope = self.unity.dash.reveal_application_scope() + self.addCleanup(self.unity.dash.ensure_hidden) + + # wait for "Installed" category + category = self.wait_for_category(scope, _("Installed")) + + # wait for results + self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) + results = category.get_results() + + result = results[0] + # result.preview_key() handles finding xy co-ords and key press + result.preview_key() + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) + + +class PreviewNavigateTests(DashTestCase): + """Tests that mouse navigation works with previews.""" + + def setUp(self): + super(PreviewNavigateTests, self).setUp() + gettext.install("unity-scope-applications", unicode=True) + + scope = self.unity.dash.reveal_application_scope() + self.addCleanup(self.unity.dash.ensure_hidden) + + # wait for "Installed" category + category = self.wait_for_category(scope, _("Installed")) + + # wait for results (we need 4 results to perorm the multi-navigation tests) + self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(4), timeout=20)) + + results = category.get_results() + result = results[3] # 3 so we can navigate left multiple times + result.preview(button=3) + self.assertThat(self.unity.dash.view.preview_displaying, Eventually(Equals(True))) + self.assertThat(self.unity.dash.view.get_preview_container, Eventually(NotEquals(None))) + + self.preview_container = self.unity.dash.view.get_preview_container() + + def test_navigate_left(self): + """Tests that left navigation works with previews.""" + + # wait until preview has finished animating + self.assertThat(self.preview_container.animating, Eventually(Equals(False))) + self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) + + old_navigation_complete_count = self.preview_container.navigation_complete_count + old_relative_nav_index = self.preview_container.relative_nav_index + + self.preview_container.navigate_left(1) + + self.assertThat(self.preview_container.navigation_complete_count, Eventually(Equals(old_navigation_complete_count+1))) + self.assertThat(self.preview_container.relative_nav_index, Eventually(Equals(old_relative_nav_index-1))) + + # should be one more on the left + self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) + # if we've navigated left, there should be at least one preview available on right. + self.assertThat(self.preview_container.navigate_right_enabled, Eventually(Equals(True))) + + # Test close preview after navigate + self.keyboard.press_and_release("Escape") + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_navigate_left_multi(self): + """Tests that multiple left navigation works with previews.""" + + # wait until preview has finished animating + self.assertThat(self.preview_container.animating, Eventually(Equals(False))) + self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) + + old_navigation_complete_count = self.preview_container.navigation_complete_count + old_relative_nav_index = self.preview_container.relative_nav_index + + self.preview_container.navigate_left(2) + + self.assertThat(self.preview_container.navigation_complete_count, Eventually(Equals(old_navigation_complete_count+2))) + self.assertThat(self.preview_container.relative_nav_index, Eventually(Equals(old_relative_nav_index-2))) + + # shouldnt be any previews on left. + self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(False))) + # if we've navigated left, there should be at least one preview available on right. + self.assertThat(self.preview_container.navigate_right_enabled, Eventually(Equals(True))) + + + def test_navigate_right(self): + """Tests that right navigation works with previews.""" + + # wait until preview has finished animating + self.assertThat(self.preview_container.animating, Eventually(Equals(False))) + self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) + + old_navigation_complete_count = self.preview_container.navigation_complete_count + old_relative_nav_index = self.preview_container.relative_nav_index + + self.preview_container.navigate_right(1) + + self.assertThat(self.preview_container.navigation_complete_count, Eventually(Equals(old_navigation_complete_count+1))) + self.assertThat(self.preview_container.relative_nav_index, Eventually(Equals(old_relative_nav_index+1))) + + # should be at least one more on the left + self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) + # if we've navigated right, there should be at least one preview available on left. + self.assertThat(self.preview_container.navigate_right_enabled, Eventually(Equals(True))) + + # Test close preview after navigate + self.keyboard.press_and_release("Escape") + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_navigate_right_multi(self): + """Tests that multiple right navigation works with previews.""" + + # wait until preview has finished animating + self.assertThat(self.preview_container.animating, Eventually(Equals(False))) + self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) + + old_navigation_complete_count = self.preview_container.navigation_complete_count + old_relative_nav_index = self.preview_container.relative_nav_index + + self.preview_container.navigate_right(2) + + self.assertThat(self.preview_container.navigation_complete_count, Eventually(Equals(old_navigation_complete_count+2))) + self.assertThat(self.preview_container.relative_nav_index, Eventually(Equals(old_relative_nav_index+2))) + + # if we've navigated right, there should be at least one preview available on left. + self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) + + def test_preview_refocus_close(self): + """Clicking on a preview element must not lose keyboard focus.""" + cover_art = self.get_current_preview().cover_art[0] + + # click the cover-art (this will set focus) + self.mouse.click_object(cover_art) + + self.keyboard.press_and_release("Escape") + + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_overlay_text(self): + """Fallback overlay text is internationalized, should always be valid.""" + cover_art = self.get_current_preview().cover_art[0] + self.assertThat(cover_art.overlay_text, + Eventually(Equals("No Image Available"))) + + +class PreviewClickCancelTests(DashTestCase): + """Tests that the preview closes when left, middle, and right clicking in the preview""" + + scenarios = [('Left button', {'clicked_button': 1}), + ('Middle button', {'clicked_button': 2}), + ('Right button', {'clicked_button': 3})] + + def setUp(self): + super(PreviewClickCancelTests, self).setUp() + gettext.install("unity-scope-applications") + scope = self.unity.dash.reveal_application_scope(clear_search=False) + self.addCleanup(self.unity.dash.ensure_hidden) + # Only testing an application preview for this test. + + search_string = "Software Updater" + if self.unity.dash.search_string != search_string: + self.unity.dash.clear_search() + self.keyboard.type(search_string) + + # wait for "Installed" category + category = self.wait_for_category(scope, _("Installed")) + + # wait for results + self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) + results = category.get_results() + + result = results[0] + result.preview(button=2) + self.assertThat(self.unity.dash.view.preview_displaying, Eventually(Equals(True))) + + self.preview_container = self.unity.dash.view.get_preview_container() + + def test_click_on_preview_icon_cancel_preview(self): + """Clicking with any button on preview icon must close preview.""" + icon = self.get_current_preview().icon[0] + self.assertThat(icon, NotEquals(None)) + + self.mouse.click_object(icon, button=self.clicked_button) + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_click_on_preview_image_cancel_preview(self): + """Clicking with any button on preview image must cancel the preview.""" + cover_art = self.get_current_preview().cover_art[0] + self.assertThat(cover_art, NotEquals(None)) + + self.mouse.click_object(cover_art, button=self.clicked_button) + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_click_on_preview_text_cancel_preview(self): + """Clicking with any button on some preview text must cancel the preview.""" + text = self.get_current_preview().text_boxes[0] + self.assertThat(text, NotEquals(None)) + self.mouse.click_object(text, button=self.clicked_button) + + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_click_on_preview_ratings_widget_cancel_preview(self): + """Clicking with any button on the ratings widget must cancel the preview.""" + ratings_widget = self.get_current_preview().ratings_widget[0] + self.assertThat(ratings_widget, NotEquals(None)) + self.mouse.click_object(ratings_widget, button=self.clicked_button) + + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + def test_click_on_preview_info_hint_cancel_preview(self): + """Clicking with any button on the info hint must cancel the preview.""" + info_hint = self.get_current_preview().info_hint_widget[0] + self.assertThat(info_hint, NotEquals(None)) + self.mouse.click_object(info_hint, button=self.clicked_button) + + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) + + +class DashDBusIfaceTests(DashTestCase): + """Test the Unity dash DBus interface.""" + + def test_dash_hide(self): + """Ensure we can hide the dash via HideDash() dbus method.""" + self.unity.dash.ensure_visible() + self.unity.dash.hide_dash_via_dbus() + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + self.unity.dash.ensure_hidden() + + +class DashCrossMonitorsTests(DashTestCase): + """Multi-monitor dash tests.""" + + def setUp(self): + super(DashCrossMonitorsTests, self).setUp() + if self.display.get_num_screens() < 2: + self.skipTest("This test requires more than 1 monitor.") + + def test_dash_stays_on_same_monitor(self): + """If the dash is opened, then the mouse is moved to another monitor and + the keyboard is used. The Dash must not move to that monitor. + """ + current_monitor = self.unity.dash.ideal_monitor + + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + move_mouse_to_screen((current_monitor + 1) % self.display.get_num_screens()) + self.keyboard.type("abc") + + self.assertThat(self.unity.dash.ideal_monitor, Eventually(Equals(current_monitor))) + + def test_dash_close_on_cross_monitor_click(self): + """Dash must close when clicking on a window in a different screen.""" + + self.addCleanup(self.unity.dash.ensure_hidden) + + for monitor in range(self.display.get_num_screens()-1): + move_mouse_to_screen(monitor) + self.unity.dash.ensure_visible() + + move_mouse_to_screen(monitor+1) + sleep(.5) + self.mouse.click() + + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + + def test_dash_opens_on_second_monitor_if_first_has_fullscreen_window(self): + """ The Dash must open if the mouse is over the second monitor while the + first monitor has a fullscreen window. """ + + gedit = self.process_manager.start_app("Text Editor") + monitor = gedit.get_windows()[0].monitor + self.keyboard.press_and_release('F11') + + move_mouse_to_screen((monitor + 1) % self.display.get_num_screens()) + self.keybinding("dash/reveal") + self.addCleanup(self.unity.dash.ensure_hidden) + + self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) + only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_gnome_key_grabber.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_gnome_key_grabber.py @@ -0,0 +1,165 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2014 Canonical Ltd. +# Author: William Hua +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +import dbus +import unity +import logging + +from autopilot.matchers import * +from gi.repository import GLib +from testtools.matchers import * + +log = logging.getLogger(__name__) + +class Accelerator: + + def __init__(self, accelerator=None, shortcut=None, action=0): + self.accelerator = accelerator + self.shortcut = shortcut + self.action = action + + def __str__(self): + if self.action: + return "%u '%s'" % (self.action, self.shortcut) + else: + return "'%s'" % self.shortcut + +class GnomeKeyGrabberTests(unity.tests.UnityTestCase): + """Gnome key grabber tests""" + + def setUp(self): + super(GnomeKeyGrabberTests, self).setUp() + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SessionBus() + proxy = bus.get_object('org.gnome.Shell', '/org/gnome/Shell') + self.interface = dbus.Interface(proxy, 'org.gnome.Shell') + + self.activatable = set() + self.activated = [False] + self.active = True + + def accelerator_activated(action, device, timestamp): + if self.active and action in self.activatable: + log.info('%d activated' % action) + self.activated[0] = True + + self.signal = self.interface.connect_to_signal('AcceleratorActivated', accelerator_activated) + + def tearDown(self): + self.active = False + + super(GnomeKeyGrabberTests, self).tearDown() + + def press_accelerator(self, accelerator): + self.activated[0] = False + + # Press accelerator shortcut + log.info("pressing '%s'" % accelerator.shortcut) + self.keyboard.press_and_release(accelerator.shortcut) + + loop = GLib.MainLoop() + + def wait(): + loop.quit() + return False + + GLib.timeout_add_seconds(1, wait) + loop.run() + + return self.activated[0] + + def check_accelerator(self, accelerator): + # Check that accelerator works + self.assertTrue(self.press_accelerator(accelerator)) + + # Remove accelerator + log.info('ungrabbing %s' % accelerator) + self.assertTrue(self.interface.UngrabAccelerator(accelerator.action)) + + # Check that accelerator does not work + self.assertFalse(self.press_accelerator(accelerator)) + + # Try removing accelerator + log.info('ungrabbing %s (should fail)' % accelerator) + self.assertFalse(self.interface.UngrabAccelerator(accelerator.action)) + + def test_grab_accelerators(self): + accelerators = [Accelerator('x', 'Shift+Control+x'), + Accelerator('y', 'Control+Alt+y'), + Accelerator('z', 'Shift+Alt+z')] + + actions = self.interface.GrabAccelerators([(accelerator.accelerator, 0) for accelerator in accelerators]) + + self.activatable.clear() + + for accelerator, action in zip(accelerators, actions): + accelerator.action = action + self.activatable.add(action) + log.info('grabbed %s' % accelerator) + + def clean_up_test_grab_accelerators(): + self.activatable.clear() + + for accelerator in accelerators: + log.info('unconditionally ungrabbing %s' % accelerator) + self.interface.UngrabAccelerator(accelerator.action) + + self.addCleanup(clean_up_test_grab_accelerators) + + for accelerator in accelerators: + self.check_accelerator(accelerator) + + def test_grab_accelerator(self): + accelerator = Accelerator('a', 'Shift+Control+Alt+a') + accelerator.action = self.interface.GrabAccelerator(accelerator.accelerator, 0) + + self.activatable.clear() + self.activatable.add(accelerator.action) + + log.info('grabbed %s' % accelerator) + + def clean_up_test_grab_accelerator(): + self.activatable.clear() + log.info('unconditionally ungrabbing %s' % accelerator) + self.interface.UngrabAccelerator(accelerator.action) + + self.addCleanup(clean_up_test_grab_accelerator) + + self.check_accelerator(accelerator) + + def test_grab_same_accelerator(self): + accelerators = [Accelerator('b', 'Shift+Control+Alt+b') for i in xrange(3)] + actions = self.interface.GrabAccelerators([(accelerator.accelerator, 0) for accelerator in accelerators]) + + self.activatable.clear() + + for accelerator, action in zip(accelerators, actions): + accelerator.action = action + self.activatable.add(action) + log.info('grabbed %s' % accelerator) + + def clean_up_test_grab_same_accelerator(): + self.activatable.clear() + + for accelerator in accelerators: + log.info('unconditionally ungrabbing %s' % accelerator) + self.interface.UngrabAccelerator(accelerator.action) + + self.addCleanup(clean_up_test_grab_same_accelerator) + + # Check that accelerator works + self.assertTrue(self.press_accelerator(accelerator)) + + # Remove accelerator + log.info('ungrabbing %s' % accelerator) + self.assertTrue(self.interface.UngrabAccelerator(accelerator.action)) + + for accelerator in accelerators[1:]: + self.assertFalse(self.press_accelerator(accelerator)) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_gobject_introspection.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_gobject_introspection.py @@ -0,0 +1,66 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2013 Canonical +# Author: Iain Lane +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from testtools import TestCase + +class GirTests(TestCase): + + """ Test that GOBject Intospection bindings can imported """ + + def setUp(self): + super(GirTests, self).setUp() + + def test_appindicator_import(self): + imported = False + + try: + from gi.repository import AppIndicator3 + imported = True + except ImportError: + # failed + pass + + self.assertTrue(imported) + + def test_dbusmenu_import(self): + imported = False + + try: + from gi.repository import Dbusmenu + imported = True + except ImportError: + # failed + pass + + self.assertTrue(imported) + + def test_dee_import(self): + imported = False + + try: + from gi.repository import Dee + imported = True + except ImportError: + # failed + pass + + self.assertTrue(imported) + + def test_unity_import(self): + imported = False + + try: + from gi.repository import Unity + imported = True + except ImportError: + # failed + pass + + self.assertTrue(imported) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_home_lens.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_home_lens.py @@ -0,0 +1,45 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Michal Hruby +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +from testtools.matchers import Equals +from time import sleep + +from unity.tests import UnityTestCase + + +class HomeScopeSearchTests(UnityTestCase): + """Test the command scope search bahavior.""" + + def setUp(self): + super(HomeScopeSearchTests, self).setUp() + + def tearDown(self): + self.unity.dash.ensure_hidden() + super(HomeScopeSearchTests, self).tearDown() + + def test_quick_run_app(self): + """Hitting enter runs an application even though a search might not have fully + finished yet. + + """ + if self.process_manager.app_is_running("Text Editor"): + self.process_manager.close_all_app("Text Editor") + sleep(1) + + kb = self.keyboard + self.unity.dash.ensure_visible() + kb.type("g") + self.assertThat(self.unity.dash.search_string, Eventually(Equals("g"))) + kb.type("edit", 0.1) + kb.press_and_release("Enter", 0.1) + self.addCleanup(self.process_manager.close_all_app, "Text Editor") + app_found = self.process_manager.wait_until_application_is_running("gedit.desktop", 5) + self.assertTrue(app_found) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_hud.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_hud.py @@ -0,0 +1,857 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Alex Launi, +# Marco Trevisan +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +from autopilot.display import Display, move_mouse_to_screen, is_rect_on_screen +from autopilot.testcase import multiply_scenarios +from autopilot.introspection.dbus import StateNotFoundError +from os import remove, environ +from os.path import exists +from tempfile import mktemp +from testtools.matchers import ( + Equals, + EndsWith, + GreaterThan, + LessThan, + NotEquals, + ) +from testtools.matchers import MismatchError +from time import sleep + +from unity.emulators.icons import HudLauncherIcon +from unity.tests import UnityTestCase + + +def _make_monitor_scenarios(): + num_monitors = Display.create().get_num_screens() + scenarios = [] + + if num_monitors == 1: + scenarios = [('Single Monitor', {'hud_monitor': 0})] + else: + for i in range(num_monitors): + scenarios += [('Monitor %d' % (i), {'hud_monitor': i})] + + return scenarios + + +class HudTestsBase(UnityTestCase): + + def setUp(self): + super(HudTestsBase, self).setUp() + + def tearDown(self): + self.unity.hud.ensure_hidden() + super(HudTestsBase, self).tearDown() + + def get_num_active_launcher_icons(self): + num_active = 0 + for icon in self.unity.launcher.model.get_launcher_icons(): + if icon.active and icon.visible: + num_active += 1 + return num_active + + # Unable to exit SDM without any active apps, need a placeholder. + # See bug LP:1079460 + def start_placeholder_app(self): + window_spec = { + "Title": "Placeholder application", + } + self.launch_test_window(window_spec) + + def start_menu_app(self): + window_spec = { + "Title": "Test menu application", + "Menu": ["Entry 1", "Entry 2", "Entry 3", "Entry 4", "Entry 5", "Quit"], + } + self.launch_test_window(window_spec) + + def hud_query_check(self, button_label): + try: + button = 0 + + for button in self.unity.hud.hud_buttons: + if button.label_no_formatting == button_label: + break + else: + self.keyboard.press_and_release('Down') + + self.assertThat(button.label_no_formatting, Equals(button_label)) + + if not button: + return + return button.label_no_formatting + except StateNotFoundError: + return + + +class HudBehaviorTests(HudTestsBase): + + def setUp(self): + super(HudBehaviorTests, self).setUp() + + if not environ.get('UBUNTU_MENUPROXY', ''): + self.patch_environment('UBUNTU_MENUPROXY', 'libappmenu.so') + self.hud_monitor = self.display.get_primary_screen() + move_mouse_to_screen(self.hud_monitor) + + def test_no_initial_values(self): + self.unity.hud.ensure_visible() + self.assertThat(self.unity.hud.num_buttons, Equals(0)) + self.assertThat(self.unity.hud.selected_button, Equals(0)) + + def test_check_a_values(self): + self.start_menu_app() + self.unity.hud.ensure_visible() + self.keyboard.type('e') + self.assertThat(self.unity.hud.search_string, Eventually(Equals('e'))) + self.assertThat(self.unity.hud.num_buttons, Eventually(Equals(5))) + self.assertThat(self.unity.hud.selected_button, Eventually(Equals(1))) + + def test_up_down_arrows(self): + self.start_menu_app() + self.unity.hud.ensure_visible() + self.keyboard.type('e') + self.assertThat(self.unity.hud.search_string, Eventually(Equals('e'))) + self.assertThat(self.unity.hud.num_buttons, Eventually(Equals(5))) + + self.keyboard.press_and_release('Down') + self.assertThat(self.unity.hud.selected_button, Eventually(Equals(2))) + self.keyboard.press_and_release('Down') + self.assertThat(self.unity.hud.selected_button, Eventually(Equals(3))) + self.keyboard.press_and_release('Down') + self.assertThat(self.unity.hud.selected_button, Eventually(Equals(4))) + self.keyboard.press_and_release('Down') + self.assertThat(self.unity.hud.selected_button, Eventually(Equals(5))) + # Down again stays on 5. + self.keyboard.press_and_release('Down') + self.assertThat(self.unity.hud.selected_button, Eventually(Equals(5))) + self.keyboard.press_and_release('Up') + self.assertThat(self.unity.hud.selected_button, Eventually(Equals(4))) + self.keyboard.press_and_release('Up') + self.assertThat(self.unity.hud.selected_button, Eventually(Equals(3))) + self.keyboard.press_and_release('Up') + self.assertThat(self.unity.hud.selected_button, Eventually(Equals(2))) + self.keyboard.press_and_release('Up') + self.assertThat(self.unity.hud.selected_button, Eventually(Equals(1))) + # Up again stays on 1. + self.keyboard.press_and_release('Up') + self.assertThat(self.unity.hud.selected_button, Eventually(Equals(1))) + + def test_no_reset_selected_button(self): + """Hud must not change selected button when results update over time.""" + # TODO - this test doesn't test anything. Onmy system the results never update. + # ideally we'd send artificial results to the hud from the test. + self.skipTest("This test makes no sense in its current state, needs reworking.") + + self.unity.hud.ensure_visible() + self.keyboard.type('is') + self.assertThat(self.unity.hud.search_string, Eventually(Equals('is'))) + self.keyboard.press_and_release('Down') + self.assertThat(self.unity.hud.selected_button, Eventually(Equals(2))) + # long sleep to let the service send updated results + sleep(10) + self.assertThat(self.unity.hud.selected_button, Equals(2)) + + def test_slow_tap_not_reveal_hud(self): + """A slow tap must not reveal the HUD.""" + self.keybinding("hud/reveal", 0.3) + # need a long sleep to ensure that we test after the hud controller has + # seen the keypress. + sleep(5) + self.assertThat(self.unity.hud.visible, Equals(False)) + + def test_alt_f4_doesnt_show_hud(self): + self.process_manager.start_app('Calculator') + sleep(1) + # Do a very fast Alt+F4 + self.keyboard.press_and_release("Alt+F4", 0.05) + sleep(1) + self.assertFalse(self.unity.hud.visible) + + def test_reveal_hud_with_no_apps(self): + """Hud must show even with no visible applications. + + This used to cause unity to crash (hence the lack of assertion in this test). + + """ + self.start_placeholder_app() + self.unity.window_manager.enter_show_desktop() + self.addCleanup(self.unity.window_manager.leave_show_desktop) + + self.unity.hud.ensure_visible() + self.unity.hud.ensure_hidden() + + def test_restore_focus(self): + """Ensures that once the hud is dismissed, the same application + that was focused before hud invocation is refocused. + """ + calc = self.process_manager.start_app("Calculator") + + # first ensure that the application has started and is focused + self.assertEqual(calc.is_active, True) + + self.unity.hud.ensure_visible() + self.unity.hud.ensure_hidden() + + # again ensure that the application we started is focused + self.assertEqual(calc.is_active, True) + + self.unity.hud.ensure_visible() + self.unity.hud.ensure_hidden() + # why do we do this: ??? + self.keyboard.press_and_release('Return') + sleep(1) + + self.assertEqual(calc.is_active, True) + + def test_gedit_save(self): + """Test that the 'save' action in the Hud works with GEdit.""" + + file_path = mktemp() + self.addCleanup(remove, file_path) + gedit_win = self.process_manager.start_app_window('Text Editor', files=[file_path], locale='C') + self.addCleanup(self.process_manager.close_all_app, 'Text Editor') + self.assertProperty(gedit_win, is_focused=True) + + self.keyboard.type("Test") + + self.unity.hud.ensure_visible() + + self.keyboard.type("save") + + self.hud_query_check(u'Save\u2002(File)') + + self.keyboard.press_and_release('Return') + self.addCleanup(self.keyboard.press_and_release, "Ctrl+s") + self.assertThat(self.unity.hud.visible, Eventually(Equals(False), timeout=30)) + + self.assertProperty(gedit_win, is_focused=True) + self.assertThat(lambda: exists(file_path), Eventually(Equals(True), timeout=30)) + + def test_hud_to_dash_has_key_focus(self): + """When switching from the hud to the dash you don't lose key focus.""" + self.unity.hud.ensure_visible() + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + self.keyboard.type('focus1') + self.assertThat(self.unity.dash.search_string, Eventually(Equals('focus1'))) + + def test_dash_to_hud_has_key_focus(self): + """When switching from the dash to the hud you don't lose key focus.""" + self.unity.dash.ensure_visible() + self.unity.hud.ensure_visible() + self.keyboard.type('focus2') + self.assertThat(self.unity.hud.search_string, Eventually(Equals('focus2'))) + + def test_hud_closes_on_workspace_switch(self): + """This test shows that when you switch to another workspace the hud closes.""" + if self.workspace.num_workspaces <= 1: + self.skipTest("This test requires enabled more than one workspace.") + initial_workspace = self.workspace.current_workspace + self.addCleanup(self.workspace.switch_to, initial_workspace) + self.unity.hud.ensure_visible() + self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + def test_hud_closes_on_spread(self): + """This test shows that when the spread is initiated, the hud closes.""" + # Need at least one application open for the spread to work. + self.process_manager.start_app_window("Calculator") + self.unity.hud.ensure_visible() + self.addCleanup(self.keybinding, "spread/cancel") + self.keybinding("spread/start") + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + def test_hud_closes_click_outside_geo_shrunk(self): + """ + Clicking outside the hud when it is shurnk will make it close. + Shurnk is when the hud has no results and is much smaller then normal. + """ + + self.unity.hud.ensure_visible() + (x,y,w,h) = self.unity.hud.view.geometry + self.mouse.move(w/2, h-50) + self.mouse.click() + + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + def test_hud_closes_click_outside_geo(self): + """Clicking outside of the hud will make it close.""" + + self.unity.hud.ensure_visible() + self.keyboard.type("Test") + + (x,y,w,h) = self.unity.hud.view.geometry + self.mouse.move(w/2, h+50) + self.mouse.click() + + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + def test_hud_closes_click_after_text_removed(self): + """Clicking outside of the hud after a search text has been entered and + then removed from the searchbox will make it close.""" + + self.unity.hud.ensure_visible() + self.keyboard.type("Test") + self.keyboard.press_and_release("Escape") + + (x,y,w,h) = self.unity.hud.view.geometry + self.mouse.move(w/2, h+50) + self.mouse.click() + + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + def test_alt_f4_close_hud(self): + """Hud must close on alt+F4.""" + self.unity.hud.ensure_visible() + self.keyboard.press_and_release("Alt+F4") + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + def test_alt_f4_close_hud_with_capslock_on(self): + """Hud must close on Alt+F4 even when the capslock is turned on.""" + self.keyboard.press_and_release("Caps_Lock") + self.addCleanup(self.keyboard.press_and_release, "Caps_Lock") + + self.unity.hud.ensure_visible() + self.keyboard.press_and_release("Alt+F4") + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + def test_app_activate_on_enter(self): + """Hud must close after activating a search item with Enter.""" + self.process_manager.start_app('Text Editor', locale='C') + self.addCleanup(self.process_manager.close_all_app, "Text Editor") + + self.unity.hud.ensure_visible() + + self.keyboard.type("Quit") + self.assertThat(self.unity.hud.search_string, Eventually(Equals("Quit"))) + + self.hud_query_check(u'Quit\u2002(File)') + + self.keyboard.press_and_release("Enter") + + is_running = lambda: self.process_manager.app_is_running("Text Editor") + self.assertThat(is_running, Eventually(Equals(False))) + + self.assertThat(self.unity.hud.visible, Eventually(Equals(False), timeout=30)) + + def test_hud_closes_on_escape(self): + """Hud must close on escape after searchbox is cleared""" + self.unity.hud.ensure_visible() + + self.keyboard.type("ThisText") + self.keyboard.press_and_release("Escape") + self.keyboard.press_and_release("Escape") + + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + def test_hud_closes_on_escape_shrunk(self): + """Hud must close when escape key is pressed""" + self.unity.hud.ensure_visible() + self.keyboard.press_and_release("Escape") + + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + def test_alt_arrow_keys_not_eaten(self): + """Tests that Alt+ArrowKey events are correctly passed to the + active window when Unity is not responding to them.""" + + term_win = self.process_manager.start_app_window("Terminal") + self.assertProperty(term_win, is_focused=True) + + # Here we anyway need a sleep, since even though the terminal can have + # keyboard focus, the shell inside might not be completely loaded yet + # and keystrokes might not get registered + sleep(1) + + #There's no easy way to read text from terminal, writing input + #to a text file and then reading from there works. + self.keyboard.type('echo "') + + #Terminal is receiving input with Alt+Arrowkeys + self.keyboard.press("Alt") + self.keyboard.press_and_release("Up") + self.keyboard.press_and_release("Down") + self.keyboard.press_and_release("Right") + self.keyboard.press_and_release("Left") + self.keyboard.release("Alt") + + self.keyboard.type('" > /tmp/ap_test_alt_keys') + self.addCleanup(remove, '/tmp/ap_test_alt_keys') + self.keyboard.press_and_release("Enter") + + file_contents = open('/tmp/ap_test_alt_keys', 'r').read().strip() + + self.assertThat(file_contents, Equals('ABCD')) + + def test_mouse_changes_selected_hud_button(self): + """This tests moves the mouse from the top of the screen to the bottom, this must + change the selected button from 1 to 5. + """ + self.start_menu_app() + self.unity.hud.ensure_visible() + + self.keyboard.type("e") + self.assertThat(self.unity.hud.num_buttons, Eventually(Equals(5))) + (x,y,w,h) = self.unity.hud.view.geometry + + # Specify a slower rate so that HUD can register the mouse movement properly + self.mouse.move(w/2, 0, rate=5) + self.assertThat(self.unity.hud.view.selected_button, Eventually(Equals(1))) + + self.mouse.move(w/2, h, rate=5) + self.assertThat(self.unity.hud.view.selected_button, Eventually(Equals(5))) + + def test_keyboard_steals_focus_from_mouse(self): + """This tests moves the mouse from the top of the screen to the bottom, + then it presses the keyboard up 5 times, this must change the selected button from 5 to 1. + """ + self.start_menu_app() + self.unity.hud.ensure_visible() + + self.keyboard.type("e") + self.assertThat(self.unity.hud.num_buttons, Eventually(Equals(5))) + (x,y,w,h) = self.unity.hud.view.geometry + + # Specify a slower rate so that HUD can register the mouse movement properly + self.mouse.move(w/2, 0, rate=5) + self.mouse.move(w/2, h, rate=5) + self.assertThat(self.unity.hud.view.selected_button, Eventually(Equals(5))) + + for i in range(5): + self.keyboard.press_and_release('Up') + + self.assertThat(self.unity.hud.view.selected_button, Eventually(Equals(1))) + + def test_keep_focus_on_application_opens(self): + """The Hud must keep key focus as well as stay open if an app gets opened from an external source. """ + + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + self.process_manager.start_app_window("Calculator") + sleep(1) + + self.keyboard.type("HasFocus") + self.assertThat(self.unity.hud.search_string, Eventually(Equals("HasFocus"))) + + def test_closes_mouse_down_outside(self): + """Test that a mouse down outside of the hud closes the hud.""" + + self.unity.hud.ensure_visible() + current_monitor = self.unity.hud.monitor + + #Click bottom right of the screen + w = self.display.get_screen_width() - 1 + h = self.display.get_screen_height() - 1 + self.mouse.move(w,h) + self.mouse.click() + + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + def test_closes_then_focuses_window_on_mouse_down(self): + """If 2 windows are open with 1 maximized and the non-maxmized + focused. Then from the Hud clicking on the maximized window + must focus that window and close the hud. + """ + char_win = self.process_manager.start_app("Character Map") + self.assertProperty(char_win, is_active=True) + self.keybinding("window/maximize") + calc_win = self.process_manager.start_app("Calculator") + + self.unity.hud.ensure_visible() + + # Click bottom right of the screen, but take into account the non-maximized window - + # we do not want to click on it as it focuses the wrong window + w = self.display.get_screen_width() - 1 + h = self.display.get_screen_height() - 1 + + # If the mouse is over the non-maximized window, move it away from it. + (calc_x, calc_y, calc_w, calc_h) = calc_win.get_windows()[0].geometry + if calc_x <= w <= calc_x+calc_w: + grab_padding = 15 + w = w - (calc_w + grab_padding) + + self.mouse.move(w,h) + self.mouse.click() + + self.assertProperty(char_win, is_active=True) + + def test_hud_does_not_focus_wrong_window_after_alt_tab(self): + """Test the Hud focuses the correct window after an Alt+Tab.""" + + char_win = self.process_manager.start_app('Character Map') + self.process_manager.start_app('Calculator') + + self.keybinding("switcher/reveal_normal") + self.assertProperty(char_win, is_active=True) + + self.unity.hud.ensure_visible() + self.unity.hud.ensure_hidden() + + self.assertProperty(char_win, is_active=True) + + def test_mouse_does_not_steal_button_focus(self): + """When typing in the hud the mouse must not steal button focus.""" + self.start_menu_app() + self.unity.hud.ensure_visible() + + (x,y,w,h) = self.unity.hud.view.geometry + self.mouse.move(w/4, h/4) + + self.keyboard.type("e") + self.assertThat(self.unity.hud.view.selected_button, Eventually(Equals(1))) + + def test_hud_opens_when_fullscreen_window(self): + """ The Hud must not open if a window is fullscreen. """ + gedit = self.process_manager.start_app("Text Editor") + self.keyboard.press_and_release('F11') + + monitor = gedit.get_windows()[0].monitor + move_mouse_to_screen(monitor) + + self.keybinding("hud/reveal") + self.addCleanup(self.unity.hud.ensure_hidden) + + self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) + + +class HudLauncherInteractionsTests(HudTestsBase): + + launcher_modes = [('Launcher never hide', {'launcher_autohide': False}), + ('Launcher autohide', {'launcher_autohide': True})] + + scenarios = multiply_scenarios(_make_monitor_scenarios(), launcher_modes) + + def setUp(self): + super(HudLauncherInteractionsTests, self).setUp() + # Launchers on all monitors + self.set_unity_option('num_launchers', 0) + self.set_unity_option('launcher_hide_mode', int(self.launcher_autohide)) + + move_mouse_to_screen(self.hud_monitor) + sleep(0.5) + + def test_multiple_hud_reveal_does_not_break_launcher(self): + """Multiple Hud reveals must not cause the launcher to set multiple + apps as active. + + """ + launcher = self.unity.launcher.get_launcher_for_monitor(self.hud_monitor) + + # We need an app to switch to: + self.process_manager.start_app('Character Map') + # We need an application to play with - I'll use the calculator. + self.process_manager.start_app('Calculator') + sleep(1) + + # before we start, make sure there's zero or one active icon: + num_active = self.get_num_active_launcher_icons() + self.assertThat(num_active, LessThan(2), "Invalid number of launcher icons active before test has run!") + + # reveal and hide hud several times over: + for i in range(3): + self.unity.hud.ensure_visible() + self.unity.hud.ensure_hidden() + + # click application icons for running apps in the launcher: + icon = self.unity.launcher.model.get_icon(desktop_id="gucharmap.desktop") + launcher.click_launcher_icon(icon) + + # see how many apps are marked as being active: + num_active = self.get_num_active_launcher_icons() + self.assertLessEqual(num_active, 1, "More than one launcher icon active after test has run!") + + def test_hud_does_not_change_launcher_status(self): + """Opening the HUD must not change the launcher visibility.""" + + launcher = self.unity.launcher.get_launcher_for_monitor(self.hud_monitor) + + launcher_shows_pre = launcher.is_showing + self.unity.hud.ensure_visible() + launcher_shows_post = launcher.is_showing + self.assertThat(launcher_shows_pre, Equals(launcher_shows_post)) + + +class HudLockedLauncherInteractionsTests(HudTestsBase): + + scenarios = _make_monitor_scenarios() + + def setUp(self): + super(HudLockedLauncherInteractionsTests, self).setUp() + # Locked Launchers on all monitors + self.set_unity_option('num_launchers', 0) + self.set_unity_option('launcher_hide_mode', 0) + + move_mouse_to_screen(self.hud_monitor) + sleep(0.5) + + def test_hud_launcher_icon_hides_bfb(self): + """BFB icon must be hidden when the HUD launcher icon is shown.""" + + hud_icon = self.unity.hud.get_launcher_icon() + bfb_icon = self.unity.launcher.model.get_bfb_icon() + + self.assertThat(bfb_icon.visible, Eventually(Equals(True))) + self.assertTrue(bfb_icon.is_on_monitor(self.hud_monitor)) + self.assertThat(hud_icon.visible, Eventually(Equals(False))) + + self.unity.hud.ensure_visible() + + self.assertTrue(hud_icon.monitors_visibility[self.hud_monitor]) + self.assertTrue(hud_icon.is_on_monitor(self.hud_monitor)) + # For some reason the BFB icon is always visible :-/ + #bfb_icon.visible, Eventually(Equals(False) + + def test_hud_desaturates_launcher_icons(self): + """Launcher icons must desaturate when the HUD is opened.""" + + self.unity.hud.ensure_visible() + + for icon in self.unity.launcher.model.get_launcher_icons_for_monitor(self.hud_monitor): + if isinstance(icon, HudLauncherIcon): + self.assertFalse(icon.monitors_desaturated[self.hud_monitor]) + else: + self.assertTrue(icon.monitors_desaturated[self.hud_monitor]) + + def test_hud_launcher_icon_click_hides_hud(self): + """Clicking the Hud Icon should hide the HUD""" + + hud_icon = self.unity.hud.get_launcher_icon() + self.unity.hud.ensure_visible() + + launcher = self.unity.launcher.get_launcher_for_monitor(self.hud_monitor) + launcher.click_launcher_icon(hud_icon) + + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + self.assertThat(hud_icon.visible, Eventually(Equals(False))) + + +class HudVisualTests(HudTestsBase): + + launcher_modes = [('Launcher never hide', {'launcher_autohide': False}), + ('Launcher autohide', {'launcher_autohide': True})] + + launcher_screen = [('Launcher on all monitors', {'launcher_primary_only': False}), + ('Launcher on primary monitor', {'launcher_primary_only': True})] + + scenarios = multiply_scenarios(_make_monitor_scenarios(), launcher_modes, launcher_screen) + + def setUp(self): + super(HudVisualTests, self).setUp() + move_mouse_to_screen(self.hud_monitor) + self.set_unity_option('launcher_hide_mode', int(self.launcher_autohide)) + self.set_unity_option('num_launchers', int(self.launcher_primary_only)) + self.hud_monitor_is_primary = (self.display.get_primary_screen() == self.hud_monitor) + self.hud_locked = (not self.launcher_autohide and (not self.launcher_primary_only or self.hud_monitor_is_primary)) + sleep(0.5) + + def test_initially_hidden(self): + self.assertFalse(self.unity.hud.visible) + + def test_hud_is_on_right_monitor(self): + """HUD must be drawn on the monitor where the mouse is.""" + self.unity.hud.ensure_visible() + self.assertThat(self.unity.hud.monitor, Eventually(Equals(self.hud_monitor))) + self.assertTrue(is_rect_on_screen(self.unity.hud.monitor, self.unity.hud.geometry)) + + def test_hud_geometries(self): + """Tests the HUD geometries for the given monitor and status.""" + self.unity.hud.ensure_visible() + monitor_geo = self.display.get_screen_geometry(self.hud_monitor) + monitor_x = monitor_geo[0] + monitor_w = monitor_geo[2] + hud_x = self.unity.hud.geometry[0] + hud_w = self.unity.hud.geometry[2] + + if self.hud_locked: + self.assertThat(hud_x, GreaterThan(monitor_x)) + self.assertThat(hud_x, LessThan(monitor_x + monitor_w)) + self.assertThat(hud_w, Equals(monitor_x + monitor_w - hud_x)) + else: + self.assertThat(hud_x, Equals(monitor_x)) + self.assertThat(hud_w, Equals(monitor_w)) + + def test_hud_is_locked_to_launcher(self): + """Tests if the HUD is locked to launcher as we expect or not.""" + self.unity.hud.ensure_visible() + self.assertThat(self.unity.hud.is_locked_launcher, Eventually(Equals(self.hud_locked))) + + def test_hud_icon_is_shown(self): + """Tests that the correct HUD icon is shown.""" + self.unity.hud.ensure_visible() + hud_launcher_icon = self.unity.hud.get_launcher_icon() + hud_embedded_icon = self.unity.hud.get_embedded_icon() + + if self.unity.hud.is_locked_launcher: + self.assertTrue(hud_launcher_icon.monitors_visibility[self.hud_monitor]) + self.assertTrue(hud_launcher_icon.is_on_monitor(self.hud_monitor)) + self.assertTrue(hud_launcher_icon.monitors_active[self.hud_monitor]) + self.assertThat(hud_launcher_icon.monitor, Equals(self.hud_monitor)) + self.assertFalse(hud_launcher_icon.desaturated) + self.assertThat(hud_embedded_icon, Equals(None)) + else: + self.assertThat(hud_launcher_icon.visible, Eventually(Equals(False))) + self.assertFalse(hud_launcher_icon.monitors_active[self.hud_monitor]) + # the embedded icon has no visible property. + self.assertThat(hud_embedded_icon, NotEquals(None)) + + def test_hud_icon_shows_the_focused_application_emblem(self): + """Tests that the correct HUD icon is shown.""" + self.process_manager.close_all_app("Calculator") + calc = self.process_manager.start_app("Calculator") + self.assertTrue(calc.is_active) + self.unity.hud.ensure_visible() + + self.assertThat(self.unity.hud.icon.icon_name, Eventually(Equals(calc.icon))) + + def test_hud_icon_shows_the_ubuntu_emblem_on_empty_desktop(self): + """When in 'show desktop' mode the hud icon must be the BFB icon.""" + self.start_placeholder_app() + self.unity.window_manager.enter_show_desktop() + self.addCleanup(self.unity.window_manager.leave_show_desktop) + self.unity.hud.ensure_visible() + + self.assertThat(self.unity.hud.icon.icon_name, Eventually(EndsWith("launcher_bfb.png"))) + + def test_switch_dash_hud_does_not_break_the_focused_application_emblem(self): + """Switching from Dash to HUD must still show the correct HUD icon.""" + self.process_manager.close_all_app("Calculator") + calc = self.process_manager.start_app("Calculator") + self.assertTrue(calc.is_active) + + self.unity.dash.ensure_visible() + self.unity.hud.ensure_visible() + + self.assertThat(self.unity.hud.icon.icon_name, Eventually(Equals(calc.icon))) + + def test_switch_hud_dash_does_not_break_the_focused_application_emblem(self): + """Switching from HUD to Dash and back must still show the correct HUD icon.""" + self.process_manager.close_all_app("Calculator") + calc = self.process_manager.start_app("Calculator") + self.assertTrue(calc.is_active) + + self.unity.hud.ensure_visible() + self.unity.dash.ensure_visible() + self.unity.hud.ensure_visible() + self.assertThat(self.unity.hud.icon.icon_name, Eventually(Equals(calc.icon))) + + def test_dash_hud_only_uses_icon_from_current_desktop(self): + """ + Switching from the dash to Hud must pick an icon from applications + from the current desktop. As the Hud must go through the entire window + stack to find the top most window. + """ + if self.workspace.num_workspaces <= 1: + self.skipTest("This test requires enabled more than one workspace.") + self.start_placeholder_app() + initial_workspace = self.workspace.current_workspace + self.addCleanup(self.workspace.switch_to, initial_workspace) + self.unity.window_manager.enter_show_desktop() + self.addCleanup(self.unity.window_manager.leave_show_desktop) + + calc = self.process_manager.start_app("Calculator") + self.assertTrue(calc.is_active) + self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) + self.unity.dash.ensure_visible() + self.unity.hud.ensure_visible() + + self.assertThat(self.unity.hud.icon.icon_name, Eventually(EndsWith("launcher_bfb.png"))) + + +class HudAlternativeKeybindingTests(HudTestsBase): + + def alternateCheck(self, keybinding="Alt", hide=False): + """Nasty workaround for LP: #1157738""" + # So, since we have no way of making sure that after changing the unity option + # the change will be already 'applied' on the system, let's try the keybinding + # a few times before deciding that it does not work and we have a regression + # Seems better than a sleep(some_value_here) + for i in range(1, 4): + try: + self.keyboard.press_and_release(keybinding) + self.assertThat(self.unity.hud.visible, Eventually(Equals(True), timeout=3)) + break + except MismatchError: + continue + if hide: + self.unity.hud.ensure_hidden() + + def test_super_h(self): + """Test hud reveal on h.""" + self.addCleanup(self.alternateCheck, hide=True) + self.set_unity_option("show_hud", "h") + # Don't use reveal_hud, but be explicit in the keybindings. + self.alternateCheck("Super+h") + self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) + + def test_ctrl_alt_h(self): + """Test hud reveal on h.""" + self.addCleanup(self.alternateCheck, hide=True) + self.set_unity_option("show_hud", "h") + # Don't use reveal_hud, but be explicit in the keybindings. + self.alternateCheck("Ctrl+Alt+h") + self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) + + +class HudCrossMonitorsTests(HudTestsBase): + """Multi-monitor hud tests.""" + + def setUp(self): + super(HudCrossMonitorsTests, self).setUp() + if self.display.get_num_screens() < 2: + self.skipTest("This test requires more than 1 monitor.") + + def test_hud_stays_on_same_monitor(self): + """If the hud is opened, then the mouse is moved to another monitor and + the keyboard is used. The hud must not move to that monitor. + """ + + current_monitor = self.unity.hud.ideal_monitor + + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + move_mouse_to_screen((current_monitor + 1) % self.display.get_num_screens()) + self.keyboard.type("abc") + + self.assertThat(self.unity.hud.ideal_monitor, Eventually(Equals(current_monitor))) + + def test_hud_close_on_cross_monitor_click(self): + """Hud must close when clicking on a window in a different screen.""" + + self.addCleanup(self.unity.hud.ensure_hidden) + + for monitor in range(self.display.get_num_screens()-1): + move_mouse_to_screen(monitor) + self.unity.hud.ensure_visible() + + move_mouse_to_screen(monitor+1) + sleep(.5) + self.mouse.click() + + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + def test_hud_opens_on_second_monitor_if_first_has_fullscreen_window(self): + """ The Hud must open if the mouse is over the second monitor while the + first monitor has a fullscreen window. """ + + gedit = self.process_manager.start_app("Text Editor") + monitor = gedit.get_windows()[0].monitor + self.keyboard.press_and_release('F11') + + move_mouse_to_screen((monitor + 1) % self.display.get_num_screens()) + self.keybinding("hud/reveal") + self.addCleanup(self.unity.hud.ensure_hidden) + + self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_ibus.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_ibus.py @@ -0,0 +1,372 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Authors: Thomi Richards, Martin Mrazik, Łukasz 'sil2100' Zemczak +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +"""Tests to ensure unity is compatible with ibus input method.""" + +from __future__ import absolute_import + +from unity.emulators.ibus import ( + get_active_input_engines, + set_active_engines, + get_available_input_engines, + get_ibus_bus, + ) +from autopilot.matchers import Eventually +from autopilot.testcase import multiply_scenarios +from testtools.matchers import Equals, NotEquals +from testtools import skip + +from unity.tests import UnityTestCase + +from gi.repository import GLib +from gi.repository import IBus +import time +import dbus +import threading + + +# See lp:ibus-query +class IBusQuery: + """A simple class allowing string queries to the IBus engine.""" + + @skip("SKIPPING TEST - TO TEST! Remove me when test is done") + def __init__(self): + self._bus = IBus.Bus() + self._dbusconn = dbus.connection.Connection(IBus.get_address()) + + # XXX: the new IBus bindings do not export create_input_context for + # introspection. This is troublesome - so, to workaround this problem + # we're directly fetching a new input context manually + ibus_obj = self._dbusconn.get_object(IBus.SERVICE_IBUS, IBus.PATH_IBUS) + self._test = dbus.Interface(ibus_obj, dbus_interface="org.freedesktop.IBus") + path = self._test.CreateInputContext("IBusQuery") + self._context = IBus.InputContext.new(path, self._bus.get_connection(), None) + + self._glibloop = GLib.MainLoop() + + self._context.connect("commit-text", self.__commit_text_cb) + self._context.connect("update-preedit-text", self.__update_preedit_cb) + self._context.connect("disabled", self.__disabled_cb) + + self._context.set_capabilities (9) + + def __commit_text_cb(self, context, text): + self.result += text.text + self._preedit = '' + + def __update_preedit_cb(self, context, text, cursor_pos, visible): + if visible: + self._preedit = text.text + + def __disabled_cb(self, a): + self.result += self._preedit + self._glibloop.quit() + + def __abort(self): + self._abort = True + + def poll(self, engine, ibus_input): + if len(ibus_input) <= 0: + return None + + self.result = '' + self._preedit = '' + self._context.focus_in() + self._context.set_engine(engine) + + # Timeout in case of the engine not being installed + self._abort = False + timeout = threading.Timer(4.0, self.__abort) + timeout.start() + while self._context.get_engine() is None: + if self._abort is True: + print "Error! Could not set the engine correctly." + return None + continue + timeout.cancel() + + for c in ibus_input: + self._context.process_key_event(ord(c), 0, 0) + + self._context.set_engine('') + self._context.focus_out() + + GLib.timeout_add_seconds(5, lambda *args: self._glibloop.quit()) + self._glibloop.run() + + return unicode(self.result, "UTF-8") + + + +class IBusTests(UnityTestCase): + """Base class for IBus tests.""" + + def setUp(self): + super(IBusTests, self).setUp() + self.set_correct_ibus_trigger_keys() + self._ibus_query = None + + def set_correct_ibus_trigger_keys(self): + """Set the correct keys to trigger IBus. + + This method configures the ibus trigger keys inside gconf, and also sets + self.activate_binding and self.activate_release_binding. + + This method adds a cleanUp to reset the old keys once the test is done. + + """ + bus = get_ibus_bus() + config = bus.get_config() + + variant = config.get_value('general/hotkey', 'triggers') + shortcuts = [] + + # If none, assume default + if variant != None: + shortcuts = variant.unpack() + else: + shortcuts = ['space'] + + # IBus uses the format 'key' + # Autopilot uses the format 'mod+mod+mod+key' + # replace all > with a +, and ignore the < char + + shortcut = "" + for c in shortcuts[0]: + if c == '>': + shortcut += '+' + elif c != '<': + shortcut += c + + self.activate_binding = shortcut + activate_release_binding_option = 'Alt+Release+Control_L' + self.activate_release_binding = 'Alt+Control_L' + + @classmethod + @skip("SKIPPING TEST - TO TEST! Remove me when test is done") + def setUpClass(cls): + cls._old_engines = None + + @classmethod + @skip("SKIPPING TEST - TO TEST! Remove me when test is done") + def tearDownClass(cls): + if cls._old_engines is not None: + set_active_engines(cls._old_engines) + bus = get_ibus_bus() + bus.exit(restart=True) + + def activate_input_engine_or_skip(self, engine_name): + """Activate the input engine 'engine_name', or skip the test if the + engine name is not avaialble (probably because it's not been installed). + + """ + available_engines = get_available_input_engines() + if engine_name in available_engines: + if get_active_input_engines() != [engine_name]: + IBusTests._old_engines = set_active_engines([engine_name]) + else: + self.skip("This test requires the '%s' engine to be installed." % (engine_name)) + + def activate_ibus(self, widget): + """Activate IBus. """ + self.keyboard.press_and_release(self.activate_binding) + + def deactivate_ibus(self, widget): + """Deactivate IBus. """ + self.keyboard.press_and_release(self.activate_binding) + + +class IBusWidgetScenariodTests(IBusTests): + """A class that includes scenarios for the hud and dash widgets.""" + + # Use lambdas here so we don't require DBus service at module import time. + scenarios = [ + ('dash', {'widget': 'dash'}), + ('hud', {'widget': 'hud'}) + ] + + def try_ibus_query(self): + """This helper method tries to query ibus, and if it has connection problems, + it restarts the ibus connection. + It is to be used in a loop until it returns True, which means we probably + got a proper result - stored in self.result + + """ + self.result = None + try: + self._ibus_query = IBusQuery() + except: + # Here is a tricky situation. Probably for some reason the ibus connection + # got busted. In this case, restarting the connection from IBusQuery is not + # enough. We have to restart the global ibus connection to be sure + self._ibus_query = None + get_ibus_bus() + return False + self.result = self._ibus_query.poll(self.engine_name, self.input) + return self.result is not None + + + def do_ibus_test(self): + """Do the basic IBus test on self.widget using self.input and self.result.""" + try: + result = self.result + except: + self.assertThat(self.try_ibus_query, Eventually(Equals(True))) + result = self.result + + widget = getattr(self.unity, self.widget) + widget.ensure_visible() + self.addCleanup(widget.ensure_hidden) + self.activate_ibus(widget.searchbar) + self.keyboard.type(self.input) + commit_key = getattr(self, 'commit_key', None) + if commit_key: + self.keyboard.press_and_release(commit_key) + self.deactivate_ibus(widget.searchbar) + self.assertThat(widget.search_string, Eventually(Equals(result))) + + + +class IBusTestsPinyin(IBusWidgetScenariodTests): + """Tests for the Pinyin(Chinese) input engine.""" + + engine_name = "pinyin" + + scenarios = multiply_scenarios( + IBusWidgetScenariodTests.scenarios, + [ + ('photo', {'input': 'zhaopian ', 'result' : u'\u7167\u7247' }), + ('internet', {'input': 'hulianwang ', 'result' : u'\u4e92\u8054\u7f51'}), + ('hello', {'input': 'ninhao ', 'result' : u'\u60a8\u597d' }), + ('management', {'input': 'guanli ', 'result' : u'\u7ba1\u7406' }), + ] + ) + + def setUp(self): + super(IBusTestsPinyin, self).setUp() + self.activate_input_engine_or_skip(self.engine_name) + + def test_pinyin(self): + self.do_ibus_test() + + +class IBusTestsHangul(IBusWidgetScenariodTests): + """Tests for the Hangul(Korean) input engine.""" + + engine_name = "hangul" + + scenarios = multiply_scenarios( + IBusWidgetScenariodTests.scenarios, + [ + ('transmission', {'input': 'xmfostmaltus ', 'result': u'\ud2b8\ub79c\uc2a4\ubbf8\uc158 '}), + ('social', {'input': 'httuf ', 'result': u'\uc18c\uc15c '}), + ('document', {'input': 'anstj ', 'result': u'\ubb38\uc11c '}), + ] + ) + + def setUp(self): + super(IBusTestsHangul, self).setUp() + self.activate_input_engine_or_skip(self.engine_name) + + def test_hangul(self): + self.do_ibus_test() + + +class IBusTestsAnthy(IBusWidgetScenariodTests): + """Tests for the Anthy(Japanese) input engine.""" + + engine_name = "anthy" + + scenarios = multiply_scenarios( + IBusWidgetScenariodTests.scenarios, + [ + ('system', {'input': 'shisutemu ', 'result' : u'\u30b7\u30b9\u30c6\u30e0' }), + ('game', {'input': 'ge-mu ', 'result' : u'\u30b2\u30fc\u30e0' }), + ('user', {'input': 'yu-za- ', 'result' : u'\u30e6\u30fc\u30b6\u30fc' }), + ], + [ + ('commit_enter', {'commit_key': 'Enter'}), + ] + ) + + def setUp(self): + super(IBusTestsAnthy, self).setUp() + self.activate_input_engine_or_skip(self.engine_name) + + def test_anthy(self): + self.do_ibus_test() + + +class IBusTestsPinyinIgnore(IBusTests): + """Tests for ignoring key events while the Pinyin input engine is active.""" + + engine_name = "pinyin" + + def setUp(self): + super(IBusTestsPinyinIgnore, self).setUp() + self.activate_input_engine_or_skip(self.engine_name) + + def test_ignore_key_events_on_dash(self): + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + self.activate_ibus(self.unity.dash.searchbar) + self.keyboard.type("cipan") + self.keyboard.press_and_release("Tab") + self.keyboard.type(" ") + self.deactivate_ibus(self.unity.dash.searchbar) + self.assertThat(self.unity.dash.search_string, Eventually(NotEquals(" "))) + + def test_ignore_key_events_on_hud(self): + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + self.keyboard.type("a") + self.activate_ibus(self.unity.hud.searchbar) + self.keyboard.type("riqi") + old_selected = self.unity.hud.selected_button + self.keyboard.press_and_release("Down") + new_selected = self.unity.hud.selected_button + self.deactivate_ibus(self.unity.hud.searchbar) + + self.assertEqual(old_selected, new_selected) + + +class IBusTestsAnthyIgnore(IBusTests): + """Tests for ignoring key events while the Anthy input engine is active.""" + + scenarios = None + engine_name = "anthy" + + def setUp(self): + super(IBusTestsAnthyIgnore, self).setUp() + self.activate_input_engine_or_skip(self.engine_name) + + def test_ignore_key_events_on_dash(self): + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + self.activate_ibus(self.unity.dash.searchbar) + self.keyboard.type("shisutemu ") + self.keyboard.press_and_release("Tab") + self.keyboard.press_and_release("Enter") + self.deactivate_ibus(self.unity.dash.searchbar) + dash_search_string = self.unity.dash.search_string + + self.assertNotEqual("", dash_search_string) + + def test_ignore_key_events_on_hud(self): + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + self.keyboard.type("a") + self.activate_ibus(self.unity.hud.searchbar) + self.keyboard.type("hiduke") + old_selected = self.unity.hud.selected_button + self.keyboard.press_and_release("Down") + new_selected = self.unity.hud.selected_button + self.deactivate_ibus(self.unity.hud.searchbar) + + self.assertEqual(old_selected, new_selected) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_panel.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_panel.py @@ -0,0 +1,1386 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.display import Display +#from autopilot.emulators.bamf import BamfWindow +from autopilot.process import Window +from autopilot.matchers import Eventually +from autopilot.testcase import multiply_scenarios +import logging +import os +from testtools.matchers import Equals, GreaterThan, NotEquals +from time import sleep + +from unity.emulators.panel import IndicatorEntry +from unity.emulators.X11 import drag_window_to_screen +from unity.tests import UnityTestCase + +import gettext + +logger = logging.getLogger(__name__) + +def _make_scenarios(): + return multiply_scenarios(_make_monitor_scenarios(), + _make_menu_modes_scenarios()) + +def _make_menu_modes_scenarios(): + return [ ('Locally Integrated Menus', {'lim': True}), + ('Global Menus', {'lim': False}) ] + +def _make_monitor_scenarios(): + num_monitors = Display.create().get_num_screens() + scenarios = [] + + if num_monitors == 1: + scenarios = [('Single Monitor', {'panel_monitor': 0})] + else: + for i in range(num_monitors): + scenarios += [('Monitor %d' % (i), {'panel_monitor': i})] + + return scenarios + +class PanelTestsBase(UnityTestCase): + + panel_monitor = 0 + lim = False + + def setUp(self): + super(PanelTestsBase, self).setUp() + self.panel = self.unity.panels.get_panel_for_monitor(self.panel_monitor) + + old_lim = self.call_gsettings_cmd('get', 'com.canonical.Unity', 'integrated-menus') + self.call_gsettings_cmd('set', 'com.canonical.Unity', 'integrated-menus', str(self.lim).lower()) + self.addCleanup(self.call_gsettings_cmd, 'set', 'com.canonical.Unity', 'integrated-menus', old_lim) + + old_showmenus = self.call_gsettings_cmd('get', 'com.canonical.Unity', 'always-show-menus') + self.call_gsettings_cmd('set', 'com.canonical.Unity', 'always-show-menus', 'false') + self.addCleanup(self.call_gsettings_cmd, 'set', 'com.canonical.Unity', 'always-show-menus', old_showmenus) + + self.panel.move_mouse_below_the_panel() + self.addCleanup(self.panel.move_mouse_below_the_panel) + + self.assertThat(self.panel.menus.integrated_menus, Eventually(Equals(self.lim))) + if not self.lim: + self.assertThat(self.panel.focused, Eventually(Equals(True))) + + def ensure_window_state(self, win, maximized=False): + if maximized and not win.is_maximized: + self.keybinding("window/maximize") + self.addCleanup(self.keybinding, "window/restore") + elif not maximized and win.is_maximized: + self.keybinding("window/restore") + self.addCleanup(self.keybinding, "window/maximize") + + self.assertThat(lambda: win.is_maximized, Eventually(Equals(maximized))) + + def open_new_application_window(self, app_name, maximized=False, move_to_monitor=True): + """Opens a new instance of the requested application, ensuring that only + one window is opened. + + Returns the opened BamfWindow + + """ + self.process_manager.close_all_app(app_name) + app_win = self.process_manager.start_app_window(app_name, locale="C") + app = app_win.application + + app_win.set_focus() + self.assertThat(lambda: app.is_active, Eventually(Equals(True))) + self.assertThat(lambda: app_win.is_focused, Eventually(Equals(True))) + self.assertThat(app.desktop_file, Equals(app_win.application.desktop_file)) + + if move_to_monitor: + self.move_window_to_panel_monitor(app_win) + + app_win.set_focus() + self.ensure_window_state(app_win, maximized) + + return app_win + + def move_window_to_panel_monitor(self, window, restore_position=False): + """Drags a window to another monitor, eventually restoring it before""" + if not isinstance(window, Window): + raise TypeError("Window must be a autopilot.process.Window") + + if window.monitor == self.panel_monitor: + return + + if window.is_maximized: + self.keybinding("window/restore") + self.addCleanup(self.keybinding, "window/maximize") + sleep(.1) + + if restore_position: + self.addCleanup(drag_window_to_screen, window, window.monitor) + + drag_window_to_screen(window, self.panel_monitor) + sleep(.25) + self.assertThat(window.monitor, Equals(self.panel_monitor)) + + def mouse_open_indicator(self, indicator): + """This is an utility function that safely opens an indicator, + ensuring that it is closed at the end of the test and that the pointer + is moved outside the panel area (to make the panel hide the menus) + """ + if not isinstance(indicator, IndicatorEntry): + raise TypeError("Window must be a IndicatorEntry") + + indicator.mouse_click() + self.addCleanup(self.panel.move_mouse_below_the_panel) + self.addCleanup(self.keyboard.press_and_release, "Escape") + self.assertThat(indicator.active, Eventually(Equals(True))) + + def assertWinButtonsInOverlayMode(self, overlay_mode): + """Assert that there are three panel window buttons and all of them are + in the specified overlay mode. + + """ + if type(overlay_mode) is not bool: + raise TypeError("overlay_mode must be True or False") + + buttons = self.panel.window_buttons.get_buttons() + self.assertThat(len(buttons), Equals(3)) + for button in buttons: + self.assertThat(button.overlay_mode, Eventually(Equals(overlay_mode))) + + def assertNoWindowOpenWithXid(self, x_id): + """Assert that Bamf doesn't know of any open windows with the given xid.""" + # We can't check text_win.closed since we've just destroyed the window. + # Instead we make sure no window with it's x_id exists. + refresh_fn = lambda: [w for w in self.process_manager.get_open_windows() if w.x_id == x_id] + self.assertThat(refresh_fn, Eventually(Equals([]))) + + def sleep_menu_settle_period(self): + """Sleep long enough for the menus to fade in and fade out again.""" + if not self.lim: + sleep(self.panel.menus.fadein_duration / 1000.0) + sleep(self.panel.menus.discovery_duration) + sleep(self.panel.menus.fadeout_duration / 1000.0) + + # Unable to exit SDM without any active apps, need a placeholder. + # See bug LP:1079460 + def start_placeholder_app(self): + window_spec = { + "Title": "Placeholder application", + } + self.launch_test_window(window_spec) + + +class PanelTitleTests(PanelTestsBase): + + scenarios = _make_scenarios() + + def setUp(self): + super(PanelTitleTests, self).setUp() + # Locked Launchers on all monitors + self.set_unity_option('num_launchers', 0) + self.set_unity_option('launcher_hide_mode', 0) + + def test_panel_title_on_empty_desktop(self): + """With no windows shown, the panel must display the default title.""" + gettext.install("unity", unicode=True) + self.start_placeholder_app() + self.unity.window_manager.enter_show_desktop() + self.addCleanup(self.unity.window_manager.leave_show_desktop) + + self.assertThat(self.panel.desktop_is_active, Eventually(Equals(True))) + self.assertThat(self.panel.title, Equals(_("Ubuntu Desktop"))) + + def test_panel_title_with_restored_application(self): + """Panel must display application name for a non-maximised application.""" + calc_win = self.open_new_application_window("Calculator", maximized=False) + + expected = calc_win.application.name if not self.lim else '' + self.assertThat(self.panel.title, Eventually(Equals(expected))) + + def test_panel_title_with_maximized_application(self): + """Panel must display application name for a maximised application.""" + text_win = self.open_new_application_window("Text Editor", maximized=True) + + self.assertThat(self.panel.title, Eventually(Equals(text_win.title))) + self.assertThat(self.panel.focused, Eventually(Equals(True))) + + def test_panel_title_with_maximized_window_restored_child(self): + """Tests the title shown in the panel when opening the restored child of + a maximized application. + """ + text_win = self.open_new_application_window("Text Editor", maximized=True) + + # Ctrl+h opens the replace dialog. + self.keyboard.press_and_release("Ctrl+h") + self.addCleanup(self.keyboard.press_and_release, "Escape") + + self.assertThat(lambda: len(text_win.application.get_windows()), + Eventually(Equals(2))) + expected = text_win.application.name if not self.lim else text_win.title + self.assertThat(self.panel.title, Equals(expected)) + self.assertThat(self.panel.focused, Eventually(Equals(not self.lim))) + + def test_panel_shows_app_title_with_maximised_app(self): + """Tests app titles are shown in the panel with a non-focused maximized application.""" + text_win = self.open_new_application_window("Text Editor", maximized=True) + calc_win = self.open_new_application_window("Calculator", maximized=False) + + expected = calc_win.application.name if not self.lim else text_win.title + self.assertThat(self.panel.title, Eventually(Equals(expected))) + self.assertThat(self.panel.focused, Eventually(Equals(not self.lim))) + + def test_panel_title_updates_when_switching_to_maximized_app(self): + """Switching to a maximised app from a restored one must update the panel title.""" + text_win = self.open_new_application_window("Text Editor", maximized=True) + self.open_new_application_window("Calculator", maximized=False) + + icon = self.unity.launcher.model.get_icon(desktop_id=text_win.application.desktop_file) + launcher = self.unity.launcher.get_launcher_for_monitor(self.panel_monitor) + launcher.click_launcher_icon(icon) + + self.assertProperty(text_win, is_focused=True) + self.assertThat(self.panel.title, Eventually(Equals(text_win.title))) + + def test_panel_title_updates_on_maximized_window_title_changes(self): + """Panel title must change when the title of a maximized application changes.""" + text_win = self.open_new_application_window("Text Editor", maximized=True) + old_title = text_win.title + + text_win.set_focus() + self.keyboard.press_and_release("Ctrl+n") + + self.assertThat(lambda: text_win.title, Eventually(NotEquals(old_title))) + self.assertThat(self.panel.title, Eventually(Equals(text_win.title))) + + def test_panel_title_doesnt_change_with_switcher(self): + """Switching between apps must not change the Panels title.""" + calc_win = self.open_new_application_window("Calculator") + text_win = self.open_new_application_window("Text Editor") + current_title = self.panel.title + + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + self.unity.switcher.next_icon() + + self.assertThat(self.panel.title, + Eventually(Equals(current_title))) + + +class PanelWindowButtonsTests(PanelTestsBase): + + scenarios = _make_scenarios() + + def setUp(self): + super(PanelWindowButtonsTests, self).setUp() + # Locked Launchers on all monitors + self.set_unity_option('num_launchers', 0) + self.set_unity_option('launcher_hide_mode', 0) + self.always_visible = self.lim + + def test_window_buttons_dont_show_on_empty_desktop(self): + """Tests that the window buttons are not shown on clean desktop.""" + self.start_placeholder_app() + self.unity.window_manager.enter_show_desktop() + self.addCleanup(self.unity.window_manager.leave_show_desktop) + + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) + + self.panel.move_mouse_over_window_buttons() + # Sleep twice as long as the timeout, just to be sure. timeout is in + # mS, we need seconds, hence the divide by 500.0 + sleep(self.panel.menus.fadein_duration / 500.0) + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) + + def test_window_buttons_dont_show_for_restored_window(self): + """Window buttons must not show for a restored window.""" + self.open_new_application_window("Calculator") + self.panel.move_mouse_below_the_panel() + + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) + + def test_window_buttons_dont_show_for_restored_window_with_mouse_in_panel(self): + """Window buttons must not show for a restored window with the mouse in + the panel.""" + self.open_new_application_window("Calculator") + self.panel.move_mouse_over_window_buttons() + + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) + + def test_window_buttons_show_for_maximized_window_on_mouse_out(self): + """Window buttons might show for a maximized window when the mouse is + outside the panel, depending on LIM setting. + """ + self.open_new_application_window("Text Editor", maximized=True) + + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) + self.assertThat(self.panel.window_buttons.focused, Eventually(Equals(True))) + + def test_window_buttons_show_for_maximized_window_on_mouse_in(self): + """Window buttons must show when a maximized window is focused and the + mouse is over the menu-view panel areas. + + """ + self.open_new_application_window("Text Editor", maximized=True) + self.panel.move_mouse_over_window_buttons() + + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) + self.assertThat(self.panel.window_buttons.focused, Eventually(Equals(True))) + self.assertWinButtonsInOverlayMode(False) + + def test_window_buttons_show_for_maximized_unfocused_window_with_mouse_in_panel(self): + """Window buttons might show for an unfocused maximized window when the + mouse is over the panel, depending on LIM setting. + """ + self.open_new_application_window("Text Editor", maximized=True) + self.open_new_application_window("Calculator") + self.panel.move_mouse_over_window_buttons() + + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) + self.assertThat(self.panel.window_buttons.focused, Eventually(Equals(not self.lim))) + + def test_window_buttons_show_for_maximized_unfocused_window_on_mouse_out(self): + """Window buttons might show for an unfocused maximized window when the + mouse is outside the panel, depending on LIM setting. + """ + self.open_new_application_window("Text Editor", maximized=True) + self.open_new_application_window("Calculator") + + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) + self.assertThat(self.panel.window_buttons.focused, Eventually(Equals(not self.lim))) + + def test_window_buttons_show_with_dash(self): + """Window buttons must be shown when the dash is open.""" + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + for monitor in range(0, self.display.get_num_screens()): + panel = self.unity.panels.get_panel_for_monitor(monitor) + + if self.unity.dash.monitor == monitor: + self.assertThat(self.unity.dash.view.overlay_window_buttons_shown[monitor], Equals(True)) + else: + self.assertThat(self.unity.dash.view.overlay_window_buttons_shown[monitor], Equals(False)) + + def test_window_buttons_work_in_dash_after_launcher_resize(self): + """When the launcher icons are resized, the window + buttons must still work in the dash.""" + + self.set_unity_option("icon_size", 25) + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + desired_max = not self.unity.dash.view.dash_maximized + if desired_max: + self.panel.window_buttons.maximize.mouse_click() + else: + self.panel.window_buttons.unmaximize.mouse_click() + + self.assertThat(self.unity.dash.view.dash_maximized, Eventually(Equals(desired_max))) + + def test_window_buttons_show_with_hud(self): + """Window buttons must be shown when the HUD is open.""" + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + for monitor in range(0, self.display.get_num_screens()): + panel = self.unity.panels.get_panel_for_monitor(monitor) + + if self.unity.hud.monitor == monitor: + self.assertThat(self.unity.hud.view.overlay_window_buttons_shown[monitor], Equals(True)) + else: + self.assertThat(self.unity.hud.view.overlay_window_buttons_shown[monitor], Equals(False)) + + def test_window_buttons_close_button_works_for_hud(self): + """Tests that the window 'Close' actually closes the HUD.""" + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + self.panel.window_buttons.close.mouse_click() + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + def test_minimize_button_disabled_for_hud(self): + """Minimize button must be disabled for the HUD.""" + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + self.assertThat(self.panel.window_buttons.minimize.enabled, Eventually(Equals(False))) + + def test_minimize_button_does_nothing_for_hud(self): + """Minimize button must not affect the Hud.""" + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + self.panel.window_buttons.minimize.mouse_click() + + self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) + + def test_maximize_button_disabled_for_hud(self): + """Maximize button must be disabled for the HUD.""" + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + self.assertThat(self.panel.window_buttons.maximize.enabled, Eventually(Equals(False))) + + def test_maximize_button_does_nothing_for_hud(self): + """Maximize button must not affect the Hud.""" + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + self.panel.window_buttons.maximize.mouse_click() + self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) + + def test_hud_maximize_button_does_not_change_dash_form_factor(self): + """Clicking on the 'Maximize' button of the HUD must not change the dash + layout. + + See bug #939054 + """ + inital_form_factor = self.unity.dash.view.form_factor + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + self.panel.window_buttons.maximize.mouse_click() + # long sleep here to make sure that any change that might happen will + # have already happened. + sleep(5) + self.assertThat(self.unity.dash.view.form_factor, Equals(inital_form_factor)) + + def test_window_buttons_close_button_works_for_dash(self): + """Tests that the window 'Close' actually closes the Dash.""" + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + self.panel.window_buttons.close.mouse_click() + + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + + def test_minimize_button_disabled_for_dash(self): + """Tests that the 'Minimize' button is disabled for the dash.""" + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + self.assertThat(self.panel.window_buttons.minimize.enabled, Eventually(Equals(False))) + + def test_minimize_button_does_nothing_for_dash(self): + """Tests that the 'Minimize' button is disabled for the dash.""" + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + self.panel.window_buttons.minimize.mouse_click() + sleep(5) + self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) + + def test_window_buttons_maximize_or_restore_dash(self): + """Tests that the Maximize/Restore button works for the dash.""" + + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + desired_max = not self.unity.dash.view.dash_maximized + if desired_max: + self.panel.window_buttons.maximize.mouse_click() + else: + self.panel.window_buttons.unmaximize.mouse_click() + + self.assertThat(self.unity.dash.view.dash_maximized, Eventually(Equals(desired_max))) + + def test_window_buttons_active_inactive_states(self): + """Tests that the maximized/restore buttons are in the correct state when the + dash is open. Asserting states: visible, sensitive, enabled. + """ + + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + unmaximize = self.unity.dash.view.window_buttons.unmaximize + maximize = self.unity.dash.view.window_buttons.maximize + + desired_max = not self.unity.dash.view.dash_maximized + if desired_max: + active = maximize + inactive = unmaximize + else: + active = unmaximize + inactive = maximize + + self.assertThat(active.visible, Eventually(Equals(True))) + self.assertThat(active.sensitive, Eventually(Equals(True))) + self.assertThat(active.enabled, Eventually(Equals(True))) + self.assertThat(inactive.visible, Eventually(Equals(False))) + + def test_window_buttons_state_switch_on_click(self): + """Tests that when clicking the maximize/restore window button it + switchs its state from either maximize to restore, or restore to + maximize. + """ + + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + unmaximize = self.unity.dash.view.window_buttons.unmaximize + maximize = self.unity.dash.view.window_buttons.maximize + + desired_max = not self.unity.dash.view.dash_maximized + if desired_max: + active = maximize + inactive = unmaximize + else: + active = unmaximize + inactive = maximize + + active.mouse_click() + + self.assertThat(inactive.visible, Eventually(Equals(True))) + self.assertThat(inactive.sensitive, Eventually(Equals(True))) + self.assertThat(inactive.enabled, Eventually(Equals(True))) + self.assertThat(active.visible, Eventually(Equals(False))) + self.assertThat(self.unity.dash.view.dash_maximized, Eventually(Equals(desired_max))) + + def test_minimize_button_disabled_for_non_minimizable_windows(self): + """Minimize button must be disabled for windows that don't support minimization.""" + text_win = self.open_new_application_window("Text Editor", maximized=True) + + self.keyboard.press_and_release("Ctrl+S") + self.addCleanup(self.keyboard.press_and_release, "Escape") + + wins = text_win.application.get_windows() + self.assertThat(len(wins), Equals(2)) + [target_win] = [w for w in wins if w.x_id != text_win.x_id] + self.move_window_to_panel_monitor(target_win, restore_position=False) + + self.keybinding("window/maximize") + self.assertProperty(target_win, is_maximized=True) + + self.assertThat(self.panel.window_buttons.close.enabled, Eventually(Equals(True))) + self.assertThat(self.panel.window_buttons.minimize.enabled, Eventually(Equals(False))) + + def test_window_buttons_show_when_indicator_active_and_mouse_over_panel(self): + """Window buttons must be shown when mouse is over panel area with an + indicator open. + """ + self.open_new_application_window("Text Editor", maximized=True) + + indicator = self.panel.indicators.get_indicator_by_name_hint("indicator-session") + self.mouse_open_indicator(indicator) + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) + + self.panel.move_mouse_below_the_panel() + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) + + self.panel.move_mouse_over_grab_area() + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) + + def test_window_buttons_show_when_holding_show_menu_key(self): + """Window buttons must show when we press the show-menu keybinding.""" + self.open_new_application_window("Text Editor", maximized=True) + + self.sleep_menu_settle_period() + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) + + self.keybinding_hold("panel/show_menus") + self.addCleanup(self.keybinding_release, "panel/show_menus") + + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) + + # Sleep a bit to avoid a race with the Hud showing + sleep(0.5) + self.keybinding_release("panel/show_menus") + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) + + def test_window_buttons_cant_accept_keynav_focus(self): + """On a mouse down event over the window buttons + you must still be able to type into the Hud. + + """ + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + self.keyboard.type("Hello") + self.panel.window_buttons.minimize.mouse_click() + self.keyboard.type("World") + + self.assertThat(self.unity.hud.search_string, Eventually(Equals("HelloWorld"))) + + +class PanelWindowButtonsActionsTests(PanelTestsBase): + """Test WindowButtons actions on windows, focused or not depending on LIM""" + restored_on_top = False + scenarios = multiply_scenarios(_make_monitor_scenarios(), + (_make_menu_modes_scenarios() + + [('Locally Integrated Menus with restored on Top', + {'lim': True, 'restored_on_top': True})])) + + def setUp(self): + super(PanelWindowButtonsActionsTests, self).setUp() + self.text_win = self.open_new_application_window("Text Editor", maximized=True) + + if self.restored_on_top: + self.open_new_application_window("Calculator") + + def test_window_buttons_update_visual_state(self): + """Window button must update its state in response to mouse events.""" + self.panel.move_mouse_over_window_buttons() + button = self.panel.window_buttons.unmaximize + + self.assertThat(self.panel.window_buttons.focused, Eventually(Equals(not self.restored_on_top))) + self.assertThat(button.visual_state, Eventually(Equals("normal"))) + + button.mouse_move_to() + self.assertThat(button.visual_state, Eventually(Equals("prelight"))) + + self.mouse.press() + self.addCleanup(self.mouse.release) + self.assertThat(button.visual_state, Eventually(Equals("pressed"))) + + def test_window_buttons_cancel(self): + """Window buttons must ignore clicks when the mouse released outside + their area. + """ + self.panel.move_mouse_over_window_buttons() + + button = self.panel.window_buttons.unmaximize + button.mouse_move_to() + self.mouse.press() + self.assertThat(button.visual_state, Eventually(Equals("pressed"))) + self.panel.move_mouse_below_the_panel() + self.mouse.release() + + self.assertThat(self.text_win.is_maximized, Equals(True)) + + def test_window_buttons_close_button_works_for_window(self): + """Close window button must actually closes a window.""" + win_xid = self.text_win.x_id + + self.panel.window_buttons.close.mouse_click() + self.assertNoWindowOpenWithXid(win_xid) + + def test_window_buttons_close_follows_fitts_law(self): + """Tests that the 'Close' button is activated when clicking at 0,0. + + See bug #839690 + """ + win_xid = self.text_win.x_id + + self.panel.move_mouse_over_window_buttons() + screen_x, screen_y = self.display.get_screen_geometry(self.panel_monitor)[:2] + self.mouse.move(screen_x, screen_y) + self.mouse.click() + + self.assertNoWindowOpenWithXid(win_xid) + + def test_window_buttons_minimize_button_works_for_window(self): + """Tests that the window button 'Minimize' actually minimizes a window.""" + self.panel.window_buttons.minimize.mouse_click() + self.assertProperty(self.text_win, is_hidden=True) + + def test_window_buttons_minimize_follows_fitts_law(self): + """Tests that the 'Minimize' button is conform to Fitts's Law. + + See bug #839690 + """ + self.panel.move_mouse_over_window_buttons() + button = self.panel.window_buttons.minimize + target_x = button.x + button.width / 2 + target_y = self.display.get_screen_geometry(self.panel_monitor)[1] + self.mouse.move(target_x, target_y) + self.mouse.click() + + self.assertProperty(self.text_win, is_hidden=True) + + def test_window_buttons_unmaximize_button_works_for_window(self): + """Tests that the window button 'Unmaximize' actually unmaximizes a window.""" + self.panel.window_buttons.unmaximize.mouse_click() + + self.assertProperties(self.text_win, is_maximized=False, is_focused=True) + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) + + def test_window_buttons_unmaximize_follows_fitts_law(self): + """Tests that the 'Unmaximize' button is conform to Fitts's Law. + + See bug #839690 + """ + button = self.panel.window_buttons.unmaximize + button.mouse_move_to() + target_x = button.x + button.width / 2 + target_y = self.display.get_screen_geometry(self.panel_monitor)[1] + self.mouse.move(target_x, target_y) + sleep(1) + self.mouse.click() + + self.assertProperty(self.text_win, is_maximized=False) + + +class PanelHoverTests(PanelTestsBase): + """Tests with the mouse pointer hovering the panel area.""" + + scenarios = _make_scenarios() + + def test_only_menus_show_for_restored_window_on_mouse_in_window_btn_area(self): + """Restored windows should only show menus when the mouse is in the window + button area. + """ + self.open_new_application_window("Calculator") + self.sleep_menu_settle_period() + + self.panel.move_mouse_over_window_buttons() + + self.assertThat(self.panel.menus_shown, Eventually(Equals(not self.lim))) + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) + + def test_only_menus_show_for_restored_window_on_mouse_in_menu_area(self): + """Restored windows should only show menus when the mouse is in the window + menu area. + """ + self.open_new_application_window("Calculator") + self.sleep_menu_settle_period() + + self.panel.move_mouse_over_menus() + + self.assertThat(self.panel.menus_shown, Eventually(Equals(not self.lim))) + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) + + def test_only_menus_show_for_restored_window_on_mouse_in_grab_area(self): + """Restored windows should only show menus when the mouse is in the panel + grab area. + """ + self.open_new_application_window("Calculator") + self.sleep_menu_settle_period() + + if self.panel.grab_area.width <= 0: + self.skipTest("Grab area is too small to run test!") + + self.panel.move_mouse_over_grab_area() + + self.assertThat(self.panel.menus_shown, Eventually(Equals(not self.lim))) + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) + + def test_hovering_over_indicators_does_not_show_app_menus(self): + """Hovering the mouse over the indicators must not show app menus.""" + self.open_new_application_window("Text Editor", maximized=True) + self.sleep_menu_settle_period() + + self.panel.move_mouse_over_menus() + # This assert is repeated from above, but we use it to make sure that + # the menus are shown before we move over the indicators. + self.assertThat(self.panel.menus_shown, Eventually(Equals(True))) + + self.panel.move_mouse_over_indicators() + + self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) + + def test_menus_show_for_maximized_window_on_mouse_in_btn_area(self): + """Menus and window buttons must be shown when the mouse is in the window + button area for a maximised application. + """ + self.open_new_application_window("Text Editor", maximized=True) + self.sleep_menu_settle_period() + + self.panel.move_mouse_over_window_buttons() + + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) + self.assertThat(self.panel.menus_shown, Eventually(Equals(True))) + + def test_menus_show_for_maximized_window_on_mouse_in_menu_area(self): + """Menus and window buttons must be shown when the mouse is in the menu + area for a maximised application. + """ + self.open_new_application_window("Text Editor", maximized=True) + self.sleep_menu_settle_period() + + self.panel.move_mouse_over_menus() + + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) + self.assertThat(self.panel.menus_shown, Eventually(Equals(True))) + + def test_menus_show_for_maximized_window_on_mouse_in_grab_area(self): + """Menus and window buttons must be shown when the mouse is in the grab + area for a maximised application. + """ + if self.panel.grab_area.width <= 0: + self.skipTest("Grab area is too small to run this test!") + + self.open_new_application_window("Text Editor", maximized=True) + self.sleep_menu_settle_period() + + self.panel.move_mouse_over_grab_area() + + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) + self.assertTrue(self.panel.menus_shown, Eventually(Equals(True))) + + def test_menus_and_btns_hidden_with_mouse_over_indicators(self): + """Hovering the mouse over the indicators must hide the menus and window + buttons. + """ + self.open_new_application_window("Text Editor", maximized=True) + self.sleep_menu_settle_period() + + self.panel.move_mouse_over_menus() + # We use this assert to make sure that the menus are visible before we + # move the mouse: + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) + + self.panel.move_mouse_over_indicators() + + self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.lim))) + self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) + + def test_hovering_indicators_open_menus(self): + """Opening an indicator entry, and then hovering on other entries must + open them. + """ + self.open_new_application_window("Text Editor", maximized=self.lim) + entries = self.panel.get_indicator_entries(include_hidden_menus=True) + + self.assertThat(len(entries), GreaterThan(0)) + self.mouse_open_indicator(entries[0]) + + for entry in entries: + entry.mouse_move_to() + self.assertThat(entry.active, Eventually(Equals(True))) + self.assertThat(entry.menu_y, Eventually(NotEquals(0))) + + +class PanelMenuTests(PanelTestsBase): + + scenarios = _make_scenarios() + + def start_test_app_with_menus(self): + window_spec = { + "Title": "Test Application with Menus", + "Menu": [ + { + "Title": "&File", + "Menu": ["Open", "Save", "Save As", "Quit"] + }, + {"Title": "&Edit"}, + {"Title": "&Quit"} + ] + } + test_win = self.launch_test_window(window_spec) + test_win.set_focus() + self.move_window_to_panel_monitor(test_win) + self.ensure_window_state(test_win, maximized=self.lim) + + return test_win + + def test_menus_are_added_on_new_application(self): + """Tests that menus are added when a new application is opened.""" + self.start_test_app_with_menus() + + refresh_fn = lambda: len(self.panel.menus.get_entries()) + self.assertThat(refresh_fn, Eventually(Equals(3))) + + menu_view = self.panel.menus + self.assertThat(lambda: menu_view.get_menu_by_label("_File"), Eventually(NotEquals(None))) + self.assertThat(lambda: menu_view.get_menu_by_label("_Edit"), Eventually(NotEquals(None))) + self.assertThat(lambda: menu_view.get_menu_by_label("_Quit"), Eventually(NotEquals(None))) + + def test_menus_are_not_shown_if_the_application_has_no_menus(self): + """Applications with no menus must not show menus in the panel.""" + + test_win = self.launch_test_window() + self.move_window_to_panel_monitor(test_win) + + self.assertThat( + lambda: len(self.panel.menus.get_entries()), + Eventually(Equals(0)), + "Current panel entries are: %r" % self.panel.menus.get_entries()) + + self.panel.move_mouse_over_grab_area() + expected = test_win.application.name if not self.lim else "" + self.assertThat(self.panel.title, Eventually(Equals(expected))) + + def test_menus_shows_when_new_application_is_opened(self): + """When starting a new application, menus must first show, then hide.""" + if self.lim: + self.skipTest("Menu discovery is disabled when LIM are enabled.") + + # This test requires the window to be opened on the monitor that is being tested and + # we cannot guarantee which monitor the window will open up on. + if self.panel_monitor > 0: + self.skipTest("Cannot guarantee which monitor the window will open on.") + + self.start_test_app_with_menus() + + self.assertThat(self.panel.menus_shown, Eventually(Equals(True))) + self.sleep_menu_settle_period() + self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) + + def test_menus_dont_show_if_a_new_application_window_is_opened(self): + """This tests the menu discovery feature on new window for a know application.""" + if self.lim: + self.skipTest("Menu discovery is disabled when LIM are enabled.") + + self.open_new_application_window("Character Map") + self.sleep_menu_settle_period() + + self.process_manager.start_app("Character Map") + sleep(self.panel.menus.fadein_duration / 1000.0) + # Not using Eventually here since this is time-critical. Need to work + # out a better way to do this. + self.assertThat(self.panel.menus_shown, Equals(False)) + + def test_menus_dont_show_for_restored_window_on_mouse_out(self): + """Restored window menus must not show when the mouse is outside the + panel menu area. + """ + self.open_new_application_window("Calculator") + self.sleep_menu_settle_period() + + self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) + + def test_menus_show_for_restored_window_on_mouse_in(self): + """Restored window menus must show only when the mouse is over the panel + menu area. + """ + self.open_new_application_window("Calculator") + self.sleep_menu_settle_period() + + self.panel.move_mouse_over_menus() + + self.assertThat(self.panel.menus_shown, Eventually(Equals(not self.lim))) + + def test_menus_dont_show_for_maximized_window_on_mouse_out(self): + """Maximized window menus must not show when the mouse is outside the + panel menu area. + """ + self.open_new_application_window("Text Editor", maximized=True) + + self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) + + def test_menus_show_for_maximized_window_on_mouse_in(self): + """Maximized window menus must only show when the mouse is over the + panel menu area. + """ + self.open_new_application_window("Text Editor", maximized=True) + self.sleep_menu_settle_period() + + self.panel.move_mouse_over_menus() + self.assertThat(self.panel.menus_shown, Eventually(Equals(True))) + + def test_menus_dont_show_with_dash(self): + """Tests that menus are not showing when opening the dash.""" + self.open_new_application_window("Text Editor", maximized=True) + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) + + def test_menus_dont_show_with_hud(self): + """Tests that menus are not showing when opening the HUD.""" + self.open_new_application_window("Character Map", maximized=True) + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) + + +class PanelIndicatorEntryTests(PanelTestsBase): + """Tests for the indicator entries, including both menu and indicators.""" + + scenarios = _make_scenarios() + + def open_app_and_get_menu_entry(self): + """Open the test app and wait for the menu entry to appear.""" + self.open_new_application_window("Text Editor" if self.lim else "Calculator", + maximized=self.lim) + + refresh_fn = lambda: len(self.panel.menus.get_entries()) + self.assertThat(refresh_fn, Eventually(GreaterThan(0))) + menu_entry = self.panel.menus.get_entries()[0] + return menu_entry + + def test_menu_opens_on_click(self): + """Tests that clicking on a menu entry, opens a menu.""" + menu_entry = self.open_app_and_get_menu_entry() + self.mouse_open_indicator(menu_entry) + + self.assertThat(menu_entry.active, Eventually(Equals(True))) + self.assertThat(menu_entry.menu_x, Eventually(Equals(menu_entry.x))) + self.assertThat(menu_entry.menu_y, Eventually(Equals(self.panel.height))) + + def test_menu_opens_closes_on_click(self): + """Clicking on an open menu entru must close it again.""" + menu_entry = self.open_app_and_get_menu_entry() + self.mouse_open_indicator(menu_entry) + + # This assert is for timing purposes only: + self.assertThat(menu_entry.active, Eventually(Equals(True))) + # Make sure we wait at least enough time that the menu appeared as well + sleep(self.panel.menus.fadein_duration / 1000.0) + self.mouse.click() + + self.assertThat(menu_entry.active, Eventually(Equals(False))) + self.assertThat(menu_entry.menu_x, Eventually(Equals(0))) + self.assertThat(menu_entry.menu_y, Eventually(Equals(0))) + + def test_menu_closes_on_click_outside(self): + """Clicking outside an open menu must close it.""" + menu_entry = self.open_app_and_get_menu_entry() + self.mouse_open_indicator(menu_entry) + + # This assert is for timing purposes only: + self.assertThat(menu_entry.active, Eventually(Equals(True))) + target_x = menu_entry.menu_x + menu_entry.menu_width/2 + target_y = menu_entry.menu_y + menu_entry.menu_height + 10 + self.mouse.move(target_x, target_y) + self.mouse.click() + + self.assertThat(menu_entry.active, Eventually(Equals(False))) + self.assertThat(menu_entry.menu_x, Eventually(Equals(0))) + self.assertThat(menu_entry.menu_y, Eventually(Equals(0))) + + def test_menu_closes_on_new_focused_application(self): + """When a new app is focused, open menu should be closed only when using Global Menus.""" + menu_entry = self.open_app_and_get_menu_entry() + self.mouse_open_indicator(menu_entry) + + # This assert is for timing purposes only: + self.assertThat(menu_entry.active, Eventually(Equals(True))) + + self.open_new_application_window("Text Editor") + get_active_indicator_fn = lambda: self.unity.panels.get_active_indicator() + self.assertThat(get_active_indicator_fn, Eventually(NotEquals(None) if self.lim else Equals(None))) + + def test_indicator_opens_when_dash_is_open(self): + """When the dash is open and a click is on an indicator the dash + must close and the indicator must open. + """ + self.unity.dash.ensure_visible() + + indicator = self.panel.indicators.get_indicator_by_name_hint("indicator-session") + self.mouse_open_indicator(indicator) + + self.assertThat(indicator.active, Eventually(Equals(True))) + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + + +class PanelKeyNavigationTests(PanelTestsBase): + + scenarios = _make_scenarios() + + def get_active_indicator(self): + """Get the active indicator in a safe manner. + + This method will wait until the active indicator has been set. + + """ + get_active_indicator_fn = lambda: self.panel.get_active_indicator() + self.assertThat(get_active_indicator_fn, Eventually(NotEquals(None))) + return get_active_indicator_fn() + + def test_panel_first_menu_show_works(self): + """Pressing the open-menus keybinding must open the first indicator.""" + self.open_new_application_window("Calculator") + refresh_fn = lambda: len(self.panel.menus.get_entries()) + self.assertThat(refresh_fn, Eventually(Equals(0) if self.lim else GreaterThan(0))) + self.addCleanup(self.keyboard.press_and_release, "Escape") + self.keybinding("panel/open_first_menu") + + open_indicator = self.get_active_indicator() + expected_indicator = self.panel.get_indicator_entries(include_hidden_menus=not self.lim)[0] + self.assertThat(open_indicator.entry_id, Eventually(Equals(expected_indicator.entry_id))) + + def test_panel_hold_show_menu_works(self): + """Holding the show menu key must reveal the menu with mnemonics.""" + self.open_new_application_window("Text Editor", maximized=self.lim) + refresh_fn = lambda: len(self.panel.menus.get_entries()) + self.assertThat(refresh_fn, Eventually(GreaterThan(0))) + self.addCleanup(self.keyboard.press_and_release, "Escape") + + # Wait for menu to fade out first + self.assertThat(self.panel.menus.get_entries()[0].visible, Eventually(Equals(0))) + + self.keyboard.press("Alt") + self.addCleanup(self.keyboard.release, "Alt") + self.assertTrue(self.panel.menus.get_entries()[0].visible) + self.assertThat(self.panel.menus.get_entries()[0].label, Equals("_File")) + + def test_panel_menu_accelerators_work(self): + """Pressing a valid menu accelerator must open the correct menu item.""" + self.open_new_application_window("Text Editor", maximized=self.lim) + refresh_fn = lambda: len(self.panel.menus.get_entries()) + self.assertThat(refresh_fn, Eventually(GreaterThan(0))) + self.addCleanup(self.keyboard.press_and_release, "Escape") + self.keyboard.press_and_release("Alt+f") + + open_indicator = self.get_active_indicator() + self.assertThat(open_indicator.label, Eventually(Equals("_File"))) + + def test_panel_indicators_key_navigation_next_works(self): + """Right arrow key must open the next menu.""" + calc_win = self.open_new_application_window("Calculator") + self.assertProperty(calc_win, is_focused=True) + + available_indicators = self.panel.get_indicator_entries(include_hidden_menus=not self.lim) + + self.keybinding("panel/open_first_menu") + self.addCleanup(self.keyboard.press_and_release, "Escape") + + self.keybinding("panel/next_indicator") + open_indicator = self.get_active_indicator() + expected_indicator = available_indicators[1] + self.assertThat(open_indicator.entry_id, Eventually(Equals(expected_indicator.entry_id))) + + def test_panel_indicators_key_navigation_prev_works(self): + """Left arrow key must open the previous menu.""" + calc_win = self.open_new_application_window("Calculator") + self.assertProperty(calc_win, is_focused=True) + + available_indicators = self.panel.get_indicator_entries(include_hidden_menus=not self.lim) + + self.keybinding("panel/open_first_menu") + self.addCleanup(self.keyboard.press_and_release, "Escape") + + self.keybinding("panel/prev_indicator") + open_indicator = self.get_active_indicator() + expected_indicator = available_indicators[-1] + + self.assertThat(open_indicator.entry_id, Eventually(Equals(expected_indicator.entry_id))) + + def test_mouse_does_not_break_key_navigation(self): + """Must be able to use the mouse to open indicators after they've been + opened with the keyboard. + """ + self.open_new_application_window("Calculator") + available_indicators = self.panel.get_indicator_entries(include_hidden_menus=not self.lim) + + self.keybinding("panel/open_first_menu") + self.addCleanup(self.keyboard.press_and_release, "Escape") + + available_indicators[2].mouse_move_to() + self.assertThat(available_indicators[2].active, Eventually(Equals(True))) + + self.keybinding("panel/prev_indicator") + self.assertThat(available_indicators[1].active, Eventually(Equals(True))) + + +class PanelGrabAreaTests(PanelTestsBase): + """Panel grab area tests.""" + + scenarios = _make_scenarios() + + def move_mouse_over_grab_area(self): + self.panel.move_mouse_over_grab_area() + sleep(.1) + + def test_unmaximize_from_grab_area_works(self): + """Dragging a window down from the panel must unmaximize it.""" + text_win = self.open_new_application_window("Text Editor", maximized=True) + + self.move_mouse_over_grab_area() + self.mouse.press() + self.panel.move_mouse_below_the_panel() + self.mouse.release() + + self.assertProperty(text_win, is_maximized=False) + + def test_focus_the_maximized_window_works(self): + """Clicking on the grab area must put a maximized window in focus.""" + text_win = self.open_new_application_window("Text Editor", maximized=True) + calc_win = self.open_new_application_window("Calculator") + + self.assertProperty(text_win, is_focused=False) + self.assertProperty(calc_win, is_focused=True) + + self.mouse.click_object(self.panel.grab_area, button=1) + self.assertProperty(text_win, is_focused=True) + + def test_lower_the_maximized_window_works(self): + """Middle-clicking on the panel grab area must lower a maximized window.""" + calc_win = self.open_new_application_window("Calculator") + text_win = self.open_new_application_window("Text Editor", maximized=True) + + self.assertProperty(text_win, is_focused=True) + self.assertProperty(calc_win, is_focused=False) + + self.mouse.click_object(self.panel.grab_area, button=2) + + self.assertProperty(calc_win, is_focused=True) + + def test_panels_dont_steal_keynav_foucs_from_hud(self): + """On a mouse click event on the panel you must still be able to type into the Hud.""" + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + self.keyboard.type("Hello") + self.mouse.click_object(self.panel.grab_area) + self.keyboard.type("World") + + self.assertThat(self.unity.hud.search_string, Eventually(Equals("HelloWorld"))) + + +class PanelLimTests(PanelTestsBase): + + scenarios = _make_monitor_scenarios() + + def setUp(self): + self.lim = True + super(PanelLimTests, self).setUp() + + def test_title_focus_on_maximized_state_changes(self): + text_win = self.open_new_application_window("Text Editor", maximized=True) + self.assertThat(self.panel.focused, Eventually(Equals(True))) + self.assertThat(self.panel.title, Eventually(Equals(text_win.title))) + + self.open_new_application_window("Calculator") + self.assertThat(self.panel.focused, Eventually(Equals(False))) + self.assertThat(self.panel.title, Eventually(Equals(text_win.title))) + + text_win.set_focus() + self.assertProperty(text_win, is_focused=True) + self.assertThat(self.panel.focused, Eventually(Equals(True))) + self.assertThat(self.panel.title, Eventually(Equals(text_win.title))) + + +class PanelCrossMonitorsTests(PanelTestsBase): + """Multimonitor panel tests.""" + + def setUp(self): + super(PanelCrossMonitorsTests, self).setUp() + if self.display.get_num_screens() < 2: + self.skipTest("This test requires a multimonitor setup") + + def test_panel_title_updates_moving_window(self): + """Panel must show the title of a restored window when moved to it's monitor.""" + calc_win = self.open_new_application_window("Calculator") + + prev_monitor = None + for monitor in range(0, self.display.get_num_screens()): + if calc_win.monitor != monitor: + drag_window_to_screen(calc_win, monitor) + + if prev_monitor: + prev_panel = self.unity.panels.get_panel_for_monitor(prev_monitor) + self.assertThat(prev_panel.active, Eventually(Equals(False))) + + panel = self.unity.panels.get_panel_for_monitor(monitor) + self.assertThat(panel.active, Eventually(Equals(True))) + self.assertThat(panel.title, Eventually(Equals(calc_win.application.name))) + + prev_monitor = monitor + + def test_window_buttons_dont_show_for_maximized_window_on_mouse_in(self): + """Window buttons must not show when the mouse is hovering the panel in + other monitors. + """ + self.open_new_application_window("Text Editor", maximized=True) + self.sleep_menu_settle_period() + + for monitor in range(0, self.display.get_num_screens()): + panel = self.unity.panels.get_panel_for_monitor(monitor) + panel.move_mouse_over_window_buttons() + + self.sleep_menu_settle_period() + + if self.panel_monitor == monitor: + self.assertThat(panel.window_buttons_shown, Eventually(Equals(True))) + else: + self.assertThat(panel.window_buttons_shown, Eventually(Equals(False))) + + def test_window_buttons_dont_show_in_other_monitors_when_dash_is_open(self): + """Window buttons must not show on the panels other than the one where + the dash is opened. + """ + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + for monitor in range(0, self.display.get_num_screens()): + panel = self.unity.panels.get_panel_for_monitor(monitor) + + if self.unity.dash.monitor == monitor: + self.assertThat(self.unity.dash.view.overlay_window_buttons_shown[monitor], Equals(True)) + else: + self.assertThat(self.unity.dash.view.overlay_window_buttons_shown[monitor], Equals(False)) + + def test_window_buttons_dont_show_in_other_monitors_when_hud_is_open(self): + """Window buttons must not show on the panels other than the one where + the hud is opened. + """ + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + + for monitor in range(0, self.display.get_num_screens()): + panel = self.unity.panels.get_panel_for_monitor(monitor) + + if self.unity.hud.monitor == monitor: + self.assertThat(self.unity.hud.view.overlay_window_buttons_shown[monitor], Equals(True)) + else: + self.assertThat(self.unity.hud.view.overlay_window_buttons_shown[monitor], Equals(False)) + + def test_window_buttons_close_inactive_when_clicked_in_another_monitor(self): + """Clicking the close button must not affect the active maximized window + on another monitor. + + See bug #865701 + """ + text_win = self.open_new_application_window("Text Editor", maximized=True) + + for monitor in range(self.display.get_num_screens()): + panel = self.unity.panels.get_panel_for_monitor(monitor) + + if monitor != text_win.monitor: + panel.window_buttons.close.mouse_move_to() + panel.window_buttons.close.mouse_click() + self.assertThat(text_win.closed, Equals(False)) + + def test_window_buttons_minimize_inactive_when_clicked_in_another_monitor(self): + """Clicking the minimise button must not affect the active maximized + window on another monitor. + + See bug #865701 + """ + text_win = self.open_new_application_window("Text Editor", maximized=True) + + for monitor in range(self.display.get_num_screens()): + panel = self.unity.panels.get_panel_for_monitor(monitor) + + if monitor != text_win.monitor: + panel.window_buttons.minimize.mouse_click() + self.assertThat(text_win.is_hidden, Equals(False)) + + def test_window_buttons_unmaximize_inactive_when_clicked_in_another_monitor(self): + """Clicking the restore button must not affect the active maximized + window on another monitor. + + See bug #865701 + """ + text_win = self.open_new_application_window("Text Editor", maximized=True) + + for monitor in range(0, self.display.get_num_screens()): + panel = self.unity.panels.get_panel_for_monitor(monitor) + + if monitor != text_win.monitor: + panel.window_buttons.unmaximize.mouse_click() + self.assertThat(text_win.is_maximized, Equals(True)) + + def test_hovering_indicators_on_multiple_monitors(self): + """Opening an indicator entry and then hovering others entries must open them.""" + self.open_new_application_window("Text Editor") + + for monitor in range(0, self.display.get_num_screens()): + panel = self.unity.panels.get_panel_for_monitor(monitor) + + indicator = panel.indicators.get_indicator_by_name_hint("indicator-session") + self.mouse_open_indicator(indicator) + + entries = panel.get_indicator_entries(include_hidden_menus=True) + self.assertThat(len(entries), GreaterThan(0)) + + for entry in entries: + entry.mouse_move_to() + + if monitor != self.panel_monitor and entry.type == "menu": + # we're on the "other" monitor, so the menu should be hidden. + self.assertThat(entry.active, Eventually(Equals(False))) + self.assertThat(entry.visible, Eventually(Equals(False))) + self.assertThat(entry.menu_y, Eventually(Equals(0))) + else: + self.assertThat(entry.visible, Eventually(Equals(True))) + self.assertThat(entry.active, Eventually(Equals(True))) + self.assertThat(entry.menu_y, Eventually(NotEquals(0))) + + # Close the last indicator on the monitor + self.mouse.click() only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_quicklist.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_quicklist.py @@ -0,0 +1,434 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards, +# Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.display import move_mouse_to_screen +from autopilot.matchers import Eventually +from autopilot.introspection.dbus import StateNotFoundError +import os.path +from testtools.matchers import Contains, Equals, NotEquals, Not, Raises +from time import sleep +from xdg.DesktopEntry import DesktopEntry + +from unity.emulators.quicklist import QuicklistMenuItemLabel +from unity.tests import UnityTestCase + + +class QuicklistActionTests(UnityTestCase): + """Tests for quicklist actions.""" + + scenarios = [ + ('remmina', {'app_name': 'Remmina'}), + ] + + def open_quicklist_for_icon(self, launcher_icon): + """Open the quicklist for the given launcher icon. + + Returns the quicklist that was opened. + + """ + launcher = self.unity.launcher.get_launcher_for_monitor(0) + launcher.click_launcher_icon(launcher_icon, button=3) + self.addCleanup(self.keyboard.press_and_release, "Escape") + self.assertThat(launcher_icon.get_quicklist, Eventually(NotEquals(None))) + return launcher_icon.get_quicklist() + + def get_desktop_entry(self, application): + # load the desktop file from disk: + desktop_id = self.process_manager.KNOWN_APPS[application]['desktop-file'] + desktop_file = os.path.join('/usr/share/applications', desktop_id) + return DesktopEntry(desktop_file) + + def test_quicklist_actions(self): + """Test that all actions present in the destop file are shown in the quicklist.""" + app = self.process_manager.start_app(self.app_name) + + # get the launcher icon from the launcher: + launcher_icon = self.unity.launcher.model.get_icon(desktop_id=app.desktop_file) + self.assertThat(launcher_icon, NotEquals(None)) + + # open the icon quicklist, and get all the text labels: + de = self.get_desktop_entry(self.app_name) + ql = self.open_quicklist_for_icon(launcher_icon) + ql_item_texts = [i.text for i in ql.items if type(i) is QuicklistMenuItemLabel] + + # iterate over all the actions from the desktop file, make sure they're + # present in the quicklist texts. + for action in de.getActions(): + key = 'Desktop Action ' + action + self.assertThat(de.content, Contains(key)) + name = de.get('Name', group=key, locale=True) + self.assertThat(ql_item_texts, Contains(name)) + + def test_quicklist_action_uses_startup_notification(self): + """Tests that quicklist uses startup notification protocol.""" + self.register_nautilus() + self.addCleanup(self.close_all_windows, "Nautilus") + + self.process_manager.start_app_window("Calculator") + self.process_manager.start_app(self.app_name) + + nautilus_icon = self.unity.launcher.model.get_icon(desktop_id="org.gnome.Nautilus.desktop") + ql = self.open_quicklist_for_icon(nautilus_icon) + de = self.get_desktop_entry("Nautilus") + + new_window_action_name = de.get("Name", group="Desktop Action Window", locale=True) + self.assertThat(new_window_action_name, NotEquals(None)) + new_win_ql_item_fn = lambda : ql.get_quicklist_item_by_text(new_window_action_name) + self.assertThat(new_win_ql_item_fn, Eventually(NotEquals(None))) + new_win_ql_item = new_win_ql_item_fn() + + ql.click_item(new_win_ql_item) + + nautilus_windows_fn = lambda: self.process_manager.get_open_windows_by_application("Nautilus") + self.assertThat(lambda: len(nautilus_windows_fn()), Eventually(Equals(1))) + [nautilus_window] = nautilus_windows_fn() + + self.assertThat(new_win_ql_item.wait_until_destroyed, Not(Raises())) + + def test_quicklist_application_item_focus_last_active_window(self): + """This tests shows that when you activate a quicklist application item + only the last focused instance of that application is rasied. + + This is tested by opening 2 Mahjongg and a Calculator. + Then we activate the Calculator quicklist item. + Then we actiavte the Mahjongg launcher icon. + """ + char_win1 = self.process_manager.start_app_window("Character Map") + calc_win = self.process_manager.start_app_window("Calculator") + char_win2 = self.process_manager.start_app_window("Character Map") + + self.assertVisibleWindowStack([char_win2, calc_win, char_win1]) + + char_icon = self.unity.launcher.model.get_icon( + desktop_id=char_win1.application.desktop_file) + calc_icon = self.unity.launcher.model.get_icon( + desktop_id=calc_win.application.desktop_file) + + calc_ql = self.open_quicklist_for_icon(calc_icon) + calc_ql.get_quicklist_application_item(calc_win.application.name).mouse_click() + + self.assertProperty(calc_win, is_focused=True) + self.assertVisibleWindowStack([calc_win, char_win2, char_win1]) + + char_ql = self.open_quicklist_for_icon(char_icon) + char_ql.get_quicklist_application_item(char_win1.application.name).mouse_click() + + self.assertProperty(char_win2, is_focused=True) + self.assertVisibleWindowStack([char_win2, calc_win, char_win1]) + + def test_quicklist_application_item_initiate_spread(self): + """This tests shows that when you activate a quicklist application item + when an application window is focused, the spread is initiated. + """ + char_win1 = self.process_manager.start_app_window("Character Map") + char_win2 = self.process_manager.start_app_window("Character Map") + char_app = char_win1.application + + self.assertVisibleWindowStack([char_win2, char_win1]) + self.assertProperty(char_win2, is_focused=True) + + char_icon = self.unity.launcher.model.get_icon(desktop_id=char_app.desktop_file) + + char_ql = self.open_quicklist_for_icon(char_icon) + app_item = char_ql.get_quicklist_application_item(char_app.name) + + self.addCleanup(self.keybinding, "spread/cancel") + app_item.mouse_click() + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) + self.assertThat(self.unity.window_manager.scale_active_for_group, Eventually(Equals(True))) + + def test_quicklist_item_triggered_closes_dash(self): + """When any quicklist item is triggered it must close the dash.""" + + calc_win = self.process_manager.start_app_window("Calculator") + self.assertProperty(calc_win, is_focused=True) + + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + + calc_icon = self.unity.launcher.model.get_icon( + desktop_id=calc_win.application.desktop_file) + self.open_quicklist_for_icon(calc_icon) + + self.keyboard.press_and_release("Down") + self.keyboard.press_and_release("Enter") + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + + def test_quicklist_closes_when_hud_opens(self): + """When a quicklist is open you must still be able to open the Hud.""" + calc = self.process_manager.start_app("Calculator") + + calc_icon = self.unity.launcher.model.get_icon(desktop_id=calc.desktop_file) + self.open_quicklist_for_icon(calc_icon) + + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) + + def test_quicklist_closes_when_dash_opens(self): + """When the quicklist is open you must still be able to open the dash.""" + calc = self.process_manager.start_app("Calculator") + + calc_icon = self.unity.launcher.model.get_icon(desktop_id=calc.desktop_file) + self.open_quicklist_for_icon(calc_icon) + + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) + + def test_right_click_opens_quicklist_if_already_open(self): + """A right click to another icon in the launcher must + close the current open quicklist and open the other + icons quicklist. + lp:890991 + """ + + icons = self.unity.launcher.model.get_launcher_icons() + + icon0_ql = self.open_quicklist_for_icon(icons[0]) + self.assertThat(icon0_ql.active, Eventually(Equals(True))) + + icon1_ql = self.open_quicklist_for_icon(icons[1]) + self.assertThat(icon1_ql.active, Eventually(Equals(True))) + self.assertThat(icon0_ql.wait_until_destroyed, Not(Raises())) + + def test_right_clicking_same_icon_doesnt_reopen_ql(self): + """A right click to the same icon in the launcher must + not re-open the quicklist if already open. It must hide. + """ + + calc_win = self.process_manager.start_app_window("Calculator") + + calc_icon = self.unity.launcher.model.get_icon( + desktop_id=calc_win.application.desktop_file) + + calc_ql = self.open_quicklist_for_icon(calc_icon) + self.assertThat(calc_ql.active, Eventually(Equals(True))) + + # We've to manually open the icon this time, as when the quicklist goes away + # its Destroyed, so its None! + launcher = self.unity.launcher.get_launcher_for_monitor(0) + launcher.click_launcher_icon(calc_icon, button=3) + self.addCleanup(self.keyboard.press_and_release, "Escape") + calc_ql = calc_icon.get_quicklist() + self.assertThat(calc_ql, Equals(None)) + + +class QuicklistKeyNavigationTests(UnityTestCase): + """Tests for the quicklist key navigation.""" + + def setUp(self): + super(QuicklistKeyNavigationTests, self).setUp() + + desktop_file = self.process_manager.KNOWN_APPS["Text Editor"]["desktop-file"] + icon_refresh_fn = lambda : self.unity.launcher.model.get_icon( + desktop_id=desktop_file) + + self.ql_app = self.process_manager.start_app("Text Editor") + + self.assertThat(icon_refresh_fn, Eventually(NotEquals(None))) + self.ql_launcher_icon = icon_refresh_fn() + + self.ql_launcher = self.unity.launcher.get_launcher_for_monitor(0) + + def open_quicklist_with_mouse(self): + """Opens a quicklist with the mouse.""" + self.ql_launcher.click_launcher_icon(self.ql_launcher_icon, button=3) + self.addCleanup(self.keyboard.press_and_release, "Escape") + self.assertThat(self.ql_launcher_icon.get_quicklist, + Eventually(NotEquals(None))) + self.quicklist = self.ql_launcher_icon.get_quicklist() + self.quicklist.move_mouse_to_right() + self.assertThat(lambda: self.quicklist.selected_item, + Eventually(Equals(None))) + + def open_quicklist_with_keyboard(self): + """Opens a quicklist using the keyboard.""" + move_mouse_to_screen(0) + self.ql_launcher.key_nav_start() + self.addCleanup(self.ql_launcher.key_nav_cancel) + + self.ql_launcher.keyboard_select_icon(tooltip_text=self.ql_app.name) + self.keybinding("launcher/keynav/open-quicklist") + self.addCleanup(self.keybinding, "launcher/keynav/close-quicklist") + + self.assertThat(self.ql_launcher_icon.get_quicklist, + Eventually(NotEquals(None))) + self.quicklist = self.ql_launcher_icon.get_quicklist() + self.assertThat(lambda: self.quicklist.selected_item, + Eventually(NotEquals(None))) + + def assertCorrectItemSelected(self, item): + """Ensure the item considers itself selected and that quicklist agrees.""" + self.assertThat(item.selected, Eventually(Equals(True))) + self.assertThat(self.quicklist.selected_item, Equals(item)) + + def test_keynav_selects_first_item_when_unselected(self): + """Home key MUST select the first selectable item in a quicklist.""" + self.open_quicklist_with_mouse() + + self.keybinding("quicklist/keynav/first") + + expected_item = self.quicklist.selectable_items[0] + self.assertCorrectItemSelected(expected_item) + + def test_keynav_selects_first_item_when_selected(self): + """Home key MUST select the first selectable item in a quicklist when + another item is selected. + """ + self.open_quicklist_with_mouse() + mouse_item = self.quicklist.selectable_items[-1] + mouse_item.mouse_move_to() + self.assertThat(mouse_item.selected, Eventually(Equals(True))) + + self.keybinding("quicklist/keynav/first") + + expected_item = self.quicklist.selectable_items[0] + self.assertCorrectItemSelected(expected_item) + + def test_keynav_next_selects_first_item_when_unselected(self): + """Down key MUST select the first valid item when nothing is selected.""" + self.open_quicklist_with_mouse() + + self.keybinding("quicklist/keynav/next") + + expected_item = self.quicklist.selectable_items[0] + self.assertCorrectItemSelected(expected_item) + + def test_keynav_selects_last_item_when_unselected(self): + """End key MUST select the last selectable item in a quicklist.""" + self.open_quicklist_with_mouse() + + self.keybinding("quicklist/keynav/last") + + expected_item = self.quicklist.selectable_items[-1] + self.assertCorrectItemSelected(expected_item) + + def test_keynav_selects_last_item_when_selected(self): + """End key MUST select the last selectable item in a quicklist when + another item is selected. + """ + self.open_quicklist_with_mouse() + mouse_item = self.quicklist.selectable_items[0] + mouse_item.mouse_move_to() + self.assertThat(mouse_item.selected, Eventually(Equals(True))) + + self.keybinding("quicklist/keynav/last") + + expected_item = self.quicklist.selectable_items[-1] + self.assertCorrectItemSelected(expected_item) + + def test_keynav_prev_selects_last_item_when_unselected(self): + """Up key MUST select the last valid item when nothing is selected.""" + self.open_quicklist_with_mouse() + + self.keybinding("quicklist/keynav/prev") + + expected_item = self.quicklist.selectable_items[-1] + self.assertCorrectItemSelected(expected_item) + + def test_launcher_keynav_selects_first_item(self): + """The first selectable item of the quicklist must be selected when + opening the quicklist using the launcher key navigation. + """ + self.open_quicklist_with_keyboard() + + expected_item = self.quicklist.selectable_items[0] + self.assertCorrectItemSelected(expected_item) + + def test_keynav_next_selection_works(self): + """Down key MUST select the next valid item.""" + self.open_quicklist_with_mouse() + + for item in self.quicklist.selectable_items: + self.keybinding("quicklist/keynav/next") + self.assertCorrectItemSelected(item) + + def test_keynav_prev_selection_works(self): + """Up key MUST select the previous valid item.""" + self.open_quicklist_with_mouse() + + for item in reversed(self.quicklist.selectable_items): + self.keybinding("quicklist/keynav/prev") + self.assertCorrectItemSelected(item) + + def test_keynav_prev_is_cyclic(self): + """Up key MUST select the last item, when the first one is selected.""" + self.open_quicklist_with_mouse() + + mouse_item = self.quicklist.selectable_items[0] + mouse_item.mouse_move_to() + self.assertThat(mouse_item.selected, Eventually(Equals(True))) + + self.keybinding("quicklist/keynav/prev") + expected_item = self.quicklist.selectable_items[-1] + self.assertCorrectItemSelected(expected_item) + + def test_keynav_next_is_cyclic(self): + """Down key MUST select the first item, when the last one is selected.""" + self.open_quicklist_with_mouse() + + mouse_item = self.quicklist.selectable_items[-1] + mouse_item.mouse_move_to() + self.assertThat(mouse_item.selected, Eventually(Equals(True))) + + self.keybinding("quicklist/keynav/next") + expected_item = self.quicklist.selectable_items[0] + self.assertCorrectItemSelected(expected_item) + + def test_keynav_mouse_interaction(self): + """Tests that the interaction between key-navigation and mouse works as + expected. See bug #911561. + """ + self.open_quicklist_with_mouse() + mouse_item = self.quicklist.selectable_items[-1] + mouse_item.mouse_move_to() + self.assertThat(mouse_item.selected, Eventually(Equals(True))) + + self.keybinding("quicklist/keynav/prev") + sleep(.1) + self.keybinding("quicklist/keynav/prev") + + key_item = self.quicklist.selectable_items[-3] + self.assertCorrectItemSelected(key_item) + + # Moving the mouse horizontally doesn't change the selection + self.mouse.move(mouse_item.x + mouse_item.width - 10, mouse_item.y + mouse_item.height / 2) + self.assertThat(self.quicklist.selected_item, Equals(key_item)) + + # Moving the mouse outside doesn't change the selection + self.mouse.move(mouse_item.x + mouse_item.width + 50, mouse_item.y + mouse_item.height / 2) + self.assertThat(self.quicklist.selected_item, Equals(key_item)) + + # Moving the mouse to another entry, changes the selection + mouse_item = self.quicklist.selectable_items[-2] + mouse_item.mouse_move_to() + self.assertCorrectItemSelected(mouse_item) + + def test_moving_mouse_during_grab_select_correct_menuitem(self): + """Test that moving the mouse during grabbing selects the + correct menu item. See bug #1027955. + """ + self.open_quicklist_with_mouse() + mouse_item = self.quicklist.selectable_items[0] + mouse_item.mouse_move_to() + self.assertThat(mouse_item.selected, Eventually(Equals(True))) + + # Dragging the mouse horizontally doesn't change the selection + self.mouse.press() + self.addCleanup(self.mouse.release) + self.mouse.move(mouse_item.x + mouse_item.width - 10, mouse_item.y + mouse_item.height / 2) + self.assertThat(mouse_item.selected, Eventually(Equals(True))) + + # Moving the mouse down selects the next item + mouse_item = self.quicklist.selectable_items[1] + mouse_item.mouse_move_to() + self.assertThat(mouse_item.selected, Eventually(Equals(True))) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_search.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_search.py @@ -0,0 +1,196 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2013 Canonical +# Author: Łukasz 'sil2100' Zemczak +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +from testtools.matchers import ( + Equals, + GreaterThan, + ) + +from unity.tests import UnityTestCase + +import gettext + +# XXX: Ugly workaround for a really annoying bug (LP: #1152517) which breaks +# this test suite. So, to workaround, we toggle the dash once before doing any +# app scope search tests +workaround_scopes_load_done = False + +class SearchTestsBase(UnityTestCase): + """Base class for testing searching in search fields. + + Each deriving class should define the self.input_and_check_result() + method that takes 2 arguments: the input string and the expected + string. This method will be used during self.do_search_test(), which + should be called for every defined scenario of input and result. + """ + + def setUp(self): + super(SearchTestsBase, self).setUp() + + def start_test_app(self): + """Start the window mocker for our search testing. + + This method creates a windowmocker application with a custom name and + custom menu. We want it to have a locale-independent menu with a + more-or-less unique menu entry for HUD testing. Also, the name of + the application is rather unique too. + """ + window_spec = { + "Title": "Test menu application", + "Menu": ["Search entry", "Quit"], + } + self.launch_test_window(window_spec) + + def do_search_test(self): + """Use the input_and_check_result method for a given scenario. + + This method uses the self.input_and_check_result() method which + needs to be defined for the given test sub-class. It uses the + self.input and self.result strings as defined by a scenario. + """ + self.input_and_check_result(self.input, self.result) + + +# Scope tests + +class ApplicationScopeSearchTestBase(SearchTestsBase): + """Common class for all tests for searching in the application scope.""" + + def setUp(self): + super(ApplicationScopeSearchTestBase, self).setUp() + # XXX: Temporary workaround for LP: #1152517 + global workaround_scopes_load_done + if not workaround_scopes_load_done: + self.unity.dash.ensure_visible() + self.unity.dash.ensure_hidden() + workaround_scopes_load_done = True + + self.app_scope = self.unity.dash.reveal_application_scope() + self.addCleanup(self.unity.dash.ensure_hidden) + gettext.install("unity-lens-applications", unicode=True) + + def input_and_check_result(self, string, expected): + self.keyboard.type(string) + self.assertThat(self.unity.dash.search_string, Eventually(Equals(string))) + category = self.app_scope.get_category_by_name(_("Installed")) + refresh_results_fn = lambda: len(category.get_results()) + self.assertThat(refresh_results_fn, Eventually(GreaterThan(0))) + results = category.get_results() + found = False + for r in results: + if r.name == expected: + found = True + break + self.assertTrue(found) + + +class ApplicationScopeSearchTests(ApplicationScopeSearchTestBase): + """Simple search tests for the application scope.""" + + scenarios = [ + ('basic', {'input': 'Window Mocker', 'result': 'Window Mocker'}), + ('lowercase', {'input': 'window mocker', 'result': 'Window Mocker'}), + ('uppercase', {'input': 'WINDOW MOCKER', 'result': 'Window Mocker'}), + ('partial', {'input': 'Window Mock', 'result': 'Window Mocker'}), + ('keyword', {'input': 'arithmetic', 'result': 'Calculator'}), + ] + + def setUp(self): + super(ApplicationScopeSearchTests, self).setUp() + + def test_application_scope_search(self): + self.do_search_test() + + +class ApplicationScopeFuzzySearchTests(ApplicationScopeSearchTestBase): + """Fuzzy, erroneous search tests for the application scope. + This checks if the application scope will find the searched application + (windowmocker here, since we want some app that has the name + locale-independent) when small spelling errors are made. + """ + + scenarios = [ + ('transposition', {'input': 'Wnidow Mocker', 'result': 'Window Mocker'}), + ('duplication', {'input': 'Wiindow Mocker', 'result': 'Window Mocker'}), + ('insertion', {'input': 'Wiondow Mocker', 'result': 'Window Mocker'}), + ('deletion', {'input': 'Wndow Mocker', 'result': 'Window Mocker'}), + ] + + def setUp(self): + super(ApplicationScopeFuzzySearchTests, self).setUp() + + def test_application_scope_fuzzy_search(self): + self.do_search_test() + + + +# HUD tests + +class HudSearchTestBase(SearchTestsBase): + """Common class for all tests for searching in the HUD.""" + + def setUp(self): + super(HudSearchTestBase, self).setUp() + self.start_test_app() + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden); + + def input_and_check_result(self, string, expected): + self.keyboard.type(string) + self.assertThat(self.unity.hud.search_string, Eventually(Equals(string), timeout=30)) + def hud_query_check(): + try: + button = self.unity.hud.selected_hud_button + if not button: + return + return button.label_no_formatting + except StateNotFoundError: + return + + self.assertThat(hud_query_check, Eventually(Equals(expected), timeout=30)) + + +class HudSearchTests(HudSearchTestBase): + """Simple search tests for the HUD.""" + + scenarios = [ + ('basic', {'input': 'Search entry', 'result': 'Search entry'}), + ('lowercase', {'input': 'search entry', 'result': 'Search entry'}), + ('uppercase', {'input': 'SEARCH ENTRY', 'result': 'Search entry'}), + ('partial', {'input': 'Search ', 'result': 'Search entry'}), + ] + + def setUp(self): + super(HudSearchTests, self).setUp() + + def test_hud_search(self): + self.do_search_test() + + +class HudFuzzySearchTests(HudSearchTestBase): + """Fuzzy, erroneous search tests for the HUD. + This checks if the HUD will find the searched menu entry from our application + (windowmocker here, since we want to have unique, locale-independent menu + entries) when small spelling errors are made. + """ + + scenarios = [ + ('transposition', {'input': 'Saerch entry', 'result': 'Search entry'}), + ('duplication', {'input': 'Seearch entry', 'result': 'Search entry'}), + ('insertion', {'input': 'Seasrch entry ', 'result': 'Search entry'}), + ('deletion', {'input': 'Serch entry', 'result': 'Search entry'}), + ] + + def setUp(self): + super(HudFuzzySearchTests, self).setUp() + + def test_hud_fuzzy_search(self): + self.do_search_test() only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_shopping_lens.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_shopping_lens.py @@ -0,0 +1,114 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Brandon Schaefer +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +from testtools.matchers import Equals, GreaterThan +from time import sleep +import urllib2 +import gettext + +from unity.tests import UnityTestCase + + +class ShoppingScopeTests(UnityTestCase): + """Test the shopping scope bahavior.""" + + def setUp(self): + super(ShoppingScopeTests, self).setUp() + try: + urllib2.urlopen("http://www.google.com", timeout=2) + except urllib2.URLError, e: + self.skip("Skipping test, no internet connection") + gettext.install("unity-scope-shopping") + + def tearDown(self): + self.unity.dash.ensure_hidden() + super(ShoppingScopeTests, self).tearDown() + + def test_no_results_in_home_scope_if_empty_search(self): + """Test that the home scope contains no results if the search bar is empty.""" + self.unity.dash.ensure_visible() + scope = self.unity.dash.get_current_scope() + + results_category = scope.get_category_by_name(_("More suggestions")) + refresh_results_fn = lambda: len(results_category.get_results()) + self.assertThat(refresh_results_fn, Eventually(Equals(0))) + + def test_home_scope_has_shopping_results(self): + """Test that the home scope contains results.""" + self.unity.dash.ensure_visible() + scope = self.unity.dash.get_current_scope() + + self.keyboard.type("playstation") + results_category = scope.get_category_by_name(_("More suggestions")) + + refresh_results_fn = lambda: len(results_category.get_results()) + self.assertThat(refresh_results_fn, Eventually(GreaterThan(1), timeout=25)) + + def test_application_scope_has_shopping_results(self): + """Test that the application scope contains results.""" + self.unity.dash.reveal_application_scope() + scope = self.unity.dash.get_current_scope() + + self.keyboard.type("Text Editor") + results_category = scope.get_category_by_name(_("More suggestions")) + + refresh_results_fn = lambda: len(results_category.get_results()) + self.assertThat(refresh_results_fn, Eventually(GreaterThan(1), timeout=25)) + + def test_music_scope_has_shopping_results(self): + """Test that the music scope contains results.""" + self.unity.dash.reveal_music_scope() + scope = self.unity.dash.get_current_scope() + + self.keyboard.type("megadeth") + results_category = scope.get_category_by_name(_("More suggestions")) + + refresh_results_fn = lambda: len(results_category.get_results()) + self.assertThat(refresh_results_fn, Eventually(GreaterThan(1), timeout=25)) + + def test_preview_works_with_shopping_scope(self): + """This test shows the dash preview works with shopping scope results.""" + self.unity.dash.ensure_visible() + scope = self.unity.dash.get_current_scope() + + self.keyboard.type("playstation") + results_category = scope.get_category_by_name(_("More suggestions")) + + refresh_results_fn = lambda: len(results_category.get_results()) + self.assertThat(refresh_results_fn, Eventually(GreaterThan(1), timeout=25)) + + results = results_category.get_results() + results[0].preview() + + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) + + def test_shopping_scope_preview_navigate_right(self): + """This test shows that shopping scope results can open previews, + then move to the next shopping result. + """ + self.unity.dash.ensure_visible() + scope = self.unity.dash.get_current_scope() + + self.keyboard.type("playstation") + results_category = scope.get_category_by_name(_("More suggestions")) + + refresh_results_fn = lambda: len(results_category.get_results()) + self.assertThat(refresh_results_fn, Eventually(GreaterThan(2), timeout=25)) + + results = results_category.get_results() + results[0].preview() + + self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) + self.preview_container = self.unity.dash.view.get_preview_container() + start_index = self.preview_container.relative_nav_index + self.preview_container.navigate_right() + + self.assertThat(self.preview_container.relative_nav_index, Eventually(Equals(start_index+1))) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_shortcut_hint.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_shortcut_hint.py @@ -0,0 +1,185 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Authors: Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +from testtools.matchers import Equals +from time import sleep + +from unity.emulators.shortcut_hint import ShortcutController +from unity.tests import UnityTestCase + + +class BaseShortcutHintTests(UnityTestCase): + """Base class for the shortcut hint tests""" + + def setUp(self): + super(BaseShortcutHintTests, self).setUp() + + self.DEFAULT_WIDTH = 970; + self.DEFAULT_HEIGHT = 680; + + #self.shortcut_hint = self.get_shortcut_controller() + self.set_unity_option('shortcut_overlay', True) + self.set_unity_log_level("unity.shell.compiz", "DEBUG") + self.skip_if_monitor_too_small() + sleep(1) + + def skip_if_monitor_too_small(self): + monitor = self.display.get_primary_screen() + monitor_geo = self.display.get_screen_geometry(monitor) + monitor_w = monitor_geo[2] + monitor_h = monitor_geo[3] + launcher_width = self.unity.launcher.get_launcher_for_monitor(monitor).geometry[2] + panel_height = self.unity.panels.get_panel_for_monitor(monitor).geometry[3] + + if ((monitor_w - launcher_width) <= self.DEFAULT_WIDTH or + (monitor_h - panel_height) <= self.DEFAULT_HEIGHT): + self.skipTest("This test requires a bigger screen, to show the ShortcutHint") + + # def get_shortcut_controller(self): + # controllers = ShortcutController.get_all_instances() + # self.assertThat(len(controllers), Equals(1)) + # return controllers[0] + + def get_launcher(self): + # We could parameterise this so all tests run on both monitors (if MM is + # set up), but I think it's fine to just always use monitor primary monitor: + monitor = self.display.get_primary_screen() + return self.unity.launcher.get_launcher_for_monitor(monitor) + + +class ShortcutHintTests(BaseShortcutHintTests): + """Tests for the shortcut hint functionality in isolation.""" + + def test_shortcut_hint_reveal(self): + """Test that the shortcut hint is shown.""" + self.unity.shortcut_hint.show() + self.addCleanup(self.unity.shortcut_hint.ensure_hidden) + self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(True))) + + def test_shortcut_hint_reveal_timeout(self): + """Shortcut hint must be shown after a sufficient timeout.""" + timeout = self.unity.shortcut_hint.get_show_timeout() + self.unity.shortcut_hint.show() + self.addCleanup(self.unity.shortcut_hint.ensure_hidden) + + sleep(timeout/2.0) + self.assertThat(self.unity.shortcut_hint.visible, Equals(False)) + # This should happen after 3/4 of 'timeout': + self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(True))) + + def test_shortcut_hint_unreveal(self): + """Shortcut hint must hide when keys are released.""" + self.unity.shortcut_hint.ensure_visible() + self.unity.shortcut_hint.hide() + self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(False))) + + def test_shortcut_hint_cancel(self): + """Shortcut hint must hide when cancelled.""" + self.unity.shortcut_hint.ensure_visible() + self.unity.shortcut_hint.cancel() + self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(False))) + + def test_shortcut_hint_no_blur(self): + """""" + self.unity.shortcut_hint.ensure_visible() + self.addCleanup(self.unity.shortcut_hint.ensure_hidden) + + self.assertThat(self.unity.shortcut_hint.get_shortcut_view().bg_texture_is_valid, Eventually(Equals(True))) + + +class ShortcutHintInteractionsTests(BaseShortcutHintTests): + """Test the shortcuthint interactions with other Unity parts.""" + + def test_shortcut_hint_hide_using_unity_shortcuts(self): + """Unity shortcuts (like expo) must hide the shortcut hint.""" + self.unity.shortcut_hint.ensure_visible() + self.addCleanup(self.unity.shortcut_hint.ensure_hidden) + + self.keybinding_tap("expo/start") + self.addCleanup(self.keybinding, "expo/cancel") + + def test_shortcut_hint_hide_pressing_modifiers(self): + """Pressing a modifer key must hide the shortcut hint.""" + self.unity.shortcut_hint.ensure_visible() + self.addCleanup(self.unity.shortcut_hint.ensure_hidden) + + self.keyboard.press('Control') + + self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(False))) + + def test_launcher_switcher_next_doesnt_show_shortcut_hint(self): + """Super+Tab switcher cycling forward must not show shortcut hint.""" + switcher_timeout = self.unity.shortcut_hint.get_show_timeout() + self.unity.shortcut_hint.show() + self.addCleanup(self.unity.dash.ensure_hidden) + self.addCleanup(self.unity.shortcut_hint.ensure_hidden) + + self.addCleanup(self.keyboard.press_and_release, "Escape") + self.keybinding("launcher/switcher/next") + self.keybinding("launcher/switcher/next") + + sleep(switcher_timeout * 2) + + self.assertThat(self.unity.shortcut_hint.visible, Equals(False)) + + self.keybinding("launcher/switcher/prev") + + def test_launcher_switcher_prev_doesnt_show_shortcut_hint(self): + """Super+Tab switcher cycling backwards must not show shortcut hint.""" + switcher_timeout = self.unity.shortcut_hint.get_show_timeout() + self.unity.shortcut_hint.show() + self.addCleanup(self.unity.dash.ensure_hidden) + self.addCleanup(self.unity.shortcut_hint.ensure_hidden) + + self.addCleanup(self.keyboard.press_and_release, "Escape") + self.keybinding("launcher/switcher/prev") + self.keybinding("launcher/switcher/prev") + + sleep(switcher_timeout * 2) + + self.assertThat(self.unity.shortcut_hint.visible, Equals(False)) + + self.keybinding("launcher/switcher/next") + + def test_launcher_icons_hints_show_with_shortcut_hint(self): + """When the shortcut hint is shown also the launcer's icons hints should + be shown. + + """ + launcher = self.get_launcher() + self.unity.shortcut_hint.ensure_visible() + self.addCleanup(self.unity.shortcut_hint.ensure_hidden) + + self.assertThat(self.unity.shortcut_hint.visible, Equals(True)) + self.assertThat(launcher.shortcuts_shown, Equals(True)) + + def test_shortcut_hint_shows_with_launcher_icons_hints(self): + """When the launcher icons hints are shown also the shortcut hint should + be shown. + + """ + launcher = self.get_launcher() + launcher.keyboard_reveal_launcher() + self.addCleanup(launcher.keyboard_unreveal_launcher) + + self.assertThat(launcher.shortcuts_shown, Eventually(Equals(True))) + self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(True))) + + def test_shortcut_hint_closes_after_key_event(self): + """ The shortcut hint must close when a key event comes through.""" + + self.unity.shortcut_hint.show() + self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(True))) + + self.addCleanup(self.unity.shortcut_hint.ensure_hidden) + + self.keyboard.press_and_release("Ctrl") + self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(False))) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_showdesktop.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_showdesktop.py @@ -0,0 +1,126 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2010 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +from testtools.matchers import Equals +from testtools import skip +from time import sleep + +from unity.emulators.switcher import SwitcherDirection +from unity.tests import UnityTestCase + + +class ShowDesktopTests(UnityTestCase): + """Test the 'Show Desktop' functionality.""" + + def setUp(self): + super(ShowDesktopTests, self).setUp() + self.set_unity_log_level("unity.wm.compiz", "DEBUG") + # we need this to let the unity models update after we shutdown apps + # before we start the next test. + sleep(2) + + def launch_test_apps(self): + """Launch character map and calculator apps, and return their windows.""" + char_win = self.process_manager.start_app_window('Character Map', locale='C') + calc_win = self.process_manager.start_app_window('Calculator', locale='C') + return (char_win, calc_win) + + def test_showdesktop_hides_apps(self): + """Show Desktop keyboard shortcut must hide applications.""" + test_windows = self.launch_test_apps() + + # show desktop, verify all windows are hidden: + self.unity.window_manager.enter_show_desktop() + self.addCleanup(self.unity.window_manager.leave_show_desktop) + + for win in test_windows: + self.assertProperty(win, is_valid=True) + self.assertProperty(win, is_hidden=True) + + def test_showdesktop_unhides_apps(self): + """Show desktop shortcut must re-show all hidden apps.""" + test_windows = self.launch_test_apps() + + # show desktop, verify all windows are hidden: + self.unity.window_manager.enter_show_desktop() + self.addCleanup(self.unity.window_manager.leave_show_desktop) + + for win in test_windows: + self.assertProperty(win, is_valid=True) + self.assertProperty(win, is_hidden=True) + + # un-show desktop, verify all windows are shown: + self.unity.window_manager.leave_show_desktop() + + for win in test_windows: + self.assertProperty(win, is_valid=True) + self.assertProperty(win, is_hidden=False) + + def test_unhide_single_app(self): + """Un-hide a single app from launcher after hiding all apps.""" + charmap, calc = self.launch_test_apps() + + # show desktop, verify all windows are hidden: + self.unity.window_manager.enter_show_desktop() + self.addCleanup(self.unity.window_manager.leave_show_desktop) + + for win in (charmap, calc): + self.assertProperty(win, is_valid=True) + self.assertProperty(win, is_hidden=True) + + # We'll un-minimise the character map - find it's launcherIcon in the launcher: + charmap_icon = self.unity.launcher.model.get_icon(desktop_id="gucharmap.desktop") + if charmap_icon: + self.unity.launcher.get_launcher_for_monitor(0).click_launcher_icon(charmap_icon) + else: + self.fail("Could not find launcher icon in launcher.") + + self.assertProperty(charmap, is_hidden=False) + self.assertProperty(calc, is_hidden=True) + + # Need to re-enter show desktop since the CharMap is visible so the cleanup handlers + # get the correct show desktop state + self.unity.window_manager.enter_show_desktop() + + def test_showdesktop_closes_dash(self): + """Show Desktop must close Dash if it's open""" + test_windows = self.launch_test_apps() + self.unity.dash.ensure_visible() + + self.unity.window_manager.enter_show_desktop() + self.addCleanup(self.unity.window_manager.leave_show_desktop) + + self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + + def test_showdesktop_closes_hud(self): + """Show Desktop must close Hud if it's open""" + test_windows = self.launch_test_apps() + self.unity.hud.ensure_visible() + + self.unity.window_manager.enter_show_desktop() + self.addCleanup(self.unity.window_manager.leave_show_desktop) + + self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) + + @skip("Breaks following tests due to SDM bug") + def test_showdesktop_switcher(self): + """Show desktop item in switcher should hide all hidden apps.""" + test_windows = self.launch_test_apps() + + # show desktop, verify all windows are hidden: + self.unity.switcher.initiate() + self.unity.switcher.select_icon(SwitcherDirection.BACKWARDS, tooltip_text="Show Desktop") + self.addCleanup(self.unity.window_manager.leave_show_desktop) + self.switcher.select() + + for win in test_windows: + self.assertProperty(win, is_valid=True) + self.assertProperty(win, is_hidden=True) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_spread.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_spread.py @@ -0,0 +1,239 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.display import Display +from autopilot.matchers import Eventually +from testtools.matchers import Equals, NotEquals +from time import sleep + +from unity.tests import UnityTestCase + + +class SpreadTests(UnityTestCase): + """Spread tests""" + + def setUp(self): + super(SpreadTests, self).setUp() + self.monitor = self.display.get_primary_screen() + self.launcher = self.unity.launcher.get_launcher_for_monitor(self.display.get_primary_screen()) + + def start_test_application_windows(self, app_name, num_windows=2): + """Start a given number of windows of the requested application""" + self.process_manager.close_all_app(app_name) + windows = [] + + for i in range(num_windows): + win = self.process_manager.start_app_window(app_name) + if windows: + self.assertThat(win.application, Equals(windows[-1].application)) + + windows.append(win) + + self.assertThat(len(windows), Equals(num_windows)) + + return windows + + def initiate_spread_for_screen(self): + """Initiate the Spread for all windows""" + self.addCleanup(self.unity.window_manager.terminate_spread) + self.unity.window_manager.initiate_spread() + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) + + def initiate_spread_for_application(self, desktop_id): + """Initiate the Spread for windows of the given app""" + icon = self.unity.launcher.model.get_icon(desktop_id=desktop_id) + self.assertThat(icon, NotEquals(None)) + + self.addCleanup(self.unity.window_manager.terminate_spread) + self.launcher.click_launcher_icon(icon, move_mouse_after=False) + self.assertThat(self.unity.window_manager.scale_active_for_group, Eventually(Equals(True))) + + def get_spread_filter(self): + self.assertThat(lambda: self.unity.screen.spread_filter, Eventually(NotEquals(None))) + return self.unity.screen.spread_filter + + def assertWindowIsScaledEquals(self, xid, is_scaled): + """Assert weather a window is scaled""" + def scaled_windows_for_screen_contains_xid(): + """Predicates the window is in the list of scaled windows for the screen. + The DBus introspection object actually raises an exception if you try to look + at a window in the scaled_windows list and it's not a scaled window. Buggzorz. + """ + scaled_windows = self.unity.screen.scaled_windows + for w in scaled_windows: + try: + if xid == w.xid: + return True + except: + pass + return False + self.assertThat(scaled_windows_for_screen_contains_xid, Eventually(Equals(is_scaled))) + + def assertWindowIsClosed(self, xid): + """Assert that a window is not in the list of the open windows""" + refresh_fn = lambda: xid in [w.x_id for w in self.process_manager.get_open_windows()] + self.assertThat(refresh_fn, Eventually(Equals(False))) + + def assertLauncherIconsSaturated(self): + for icon in self.unity.launcher.model.get_launcher_icons(): + self.assertFalse(icon.monitors_desaturated[self.monitor]) + + def assertLauncherIconsDesaturated(self, also_active=True): + for icon in self.unity.launcher.model.get_launcher_icons(): + if not also_active and icon.active: + self.assertFalse(icon.monitors_desaturated[self.monitor]) + else: + self.assertTrue(icon.monitors_desaturated[self.monitor]) + + def test_scale_application_windows(self): + """All the windows of an application must be scaled when application + spread is initiated + + """ + [win1, win2] = self.start_test_application_windows("Calculator") + self.initiate_spread_for_application(win1.application.desktop_file) + + self.assertThat(lambda: len(self.unity.screen.scaled_windows), Eventually(Equals(2))) + self.assertThat(lambda: (win1.x_id and win2.x_id) in [w.xid for w in self.unity.screen.scaled_windows], + Eventually(Equals(True))) + + def test_scaled_window_is_focused_on_click(self): + """Test that a window is focused when clicked in spread""" + windows = self.start_test_application_windows("Calculator", 3) + self.initiate_spread_for_application(windows[0].application.desktop_file) + + not_focused = [w for w in windows if not w.is_focused][0] + + target_xid = not_focused.x_id + [target_win] = [w for w in self.unity.screen.scaled_windows if w.xid == target_xid] + + self.mouse.click_object(target_win, button=1) + self.assertThat(lambda: not_focused.is_focused, Eventually(Equals(True))) + + def test_scaled_window_closes_on_middle_click(self): + """Test that a window is closed when middle-clicked in spread""" + win = self.start_test_application_windows("Calculator", 2)[0] + self.initiate_spread_for_application(win.application.desktop_file) + + target_xid = win.x_id + [target_win] = [w for w in self.unity.screen.scaled_windows if w.xid == target_xid] + + sleep(1) + self.mouse.click_object(target_win, button=2) + + self.assertWindowIsScaledEquals(target_xid, False) + self.assertWindowIsClosed(target_xid) + + def test_scaled_window_closes_on_close_button_click(self): + """Test that a window is closed when its close button is clicked in spread""" + win = self.start_test_application_windows("Calculator", 1)[0] + self.initiate_spread_for_screen() + + target_xid = win.x_id + [target_win] = [w for w in self.unity.screen.scaled_windows if w.xid == target_xid] + + # Make sure mouse is over the test window + self.mouse.move_to_object(target_win) + self.mouse.click_object(target_win.scale_close_geometry) + + self.assertWindowIsScaledEquals(target_xid, False) + self.assertWindowIsClosed(target_xid) + + def test_spread_desaturate_launcher_icons(self): + """Test that the screen spread desaturates the launcher icons""" + self.start_test_application_windows("Calculator", 1) + self.initiate_spread_for_screen() + self.launcher.move_mouse_to_right_of_launcher() + self.assertLauncherIconsDesaturated() + + def test_spread_saturate_launcher_icons_on_mouse_over(self): + """Test that the screen spread re-saturates the launcher icons on mouse over""" + win = self.start_test_application_windows("Calculator", 2)[0] + self.initiate_spread_for_application(win.application.desktop_file) + self.launcher.move_mouse_over_launcher() + self.assertLauncherIconsSaturated() + + def test_app_spread_desaturate_inactive_launcher_icons(self): + """Test that the app spread desaturates the inactive launcher icons""" + win = self.start_test_application_windows("Calculator", 2)[0] + self.initiate_spread_for_application(win.application.desktop_file) + self.assertLauncherIconsDesaturated(also_active=False) + + def test_app_spread_saturate_launcher_icons_on_mouse_move(self): + """Test that the app spread re-saturates the launcher icons on mouse move""" + win = self.start_test_application_windows("Calculator", 2)[0] + self.initiate_spread_for_application(win.application.desktop_file) + self.launcher.move_mouse_to_icon(self.unity.launcher.model.get_bfb_icon()) + self.assertLauncherIconsSaturated() + + def test_app_spread_saturate_launcher_icons_on_mouse_over(self): + """Test that the app spread re-saturates the launcher icons on mouse over""" + win = self.start_test_application_windows("Calculator", 2)[0] + self.initiate_spread_for_application(win.application.desktop_file) + self.launcher.move_mouse_over_launcher() + self.assertLauncherIconsSaturated() + + def test_app_spread_desaturate_launcher_icons_switching_application(self): + """Test that the app spread desaturates the launcher icons on mouse over""" + cal_win = self.start_test_application_windows("Calculator", 2)[0] + char_win = self.start_test_application_windows("Character Map", 2)[0] + self.initiate_spread_for_application(char_win.application.desktop_file) + self.initiate_spread_for_application(cal_win.application.desktop_file) + self.assertLauncherIconsDesaturated(also_active=False) + + def test_spread_hides_icon_tooltip(self): + """Tests that the screen spread hides the active tooltip.""" + [win] = self.start_test_application_windows("Calculator", 1) + icon = self.unity.launcher.model.get_icon(desktop_id=win.application.desktop_file) + self.launcher.move_mouse_to_icon(icon) + + self.assertThat(lambda: icon.get_tooltip(), Eventually(NotEquals(None))) + self.assertThat(icon.get_tooltip().active, Eventually(Equals(True))) + + self.initiate_spread_for_screen() + self.assertThat(icon.get_tooltip(), Equals(None)) + + def test_spread_puts_panel_in_overlay_mode(self): + """Test that the panel is in overlay mode when in spread""" + self.start_test_application_windows("Calculator", 1) + self.initiate_spread_for_screen() + self.assertThat(self.unity.panels.get_active_panel().in_overlay_mode, Eventually(Equals(True))) + self.unity.window_manager.terminate_spread() + self.assertThat(self.unity.panels.get_active_panel().in_overlay_mode, Eventually(Equals(False))) + + def test_panel_close_window_button_terminates_spread(self): + """Test that the panel close window button terminates the spread""" + self.start_test_application_windows("Calculator", 1) + self.initiate_spread_for_screen() + self.unity.panels.get_active_panel().window_buttons.close.mouse_click(); + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(False))) + + def test_spread_filter(self): + """Test spread filter""" + cal_wins = self.start_test_application_windows("Calculator", 2) + char_wins = self.start_test_application_windows("Character Map", 2) + self.initiate_spread_for_screen() + spread_filter = self.get_spread_filter() + self.assertThat(spread_filter.visible, Eventually(Equals(False))) + + self.addCleanup(self.keyboard.press_and_release, "Escape") + self.keyboard.type(cal_wins[0].title) + self.assertThat(spread_filter.visible, Eventually(Equals(True))) + self.assertThat(spread_filter.search_bar.search_string, Eventually(Equals(cal_wins[0].title))) + + for w in cal_wins + char_wins: + self.assertWindowIsScaledEquals(w.x_id, (w in cal_wins)) + + self.keyboard.press_and_release("Escape") + self.assertThat(spread_filter.visible, Eventually(Equals(False))) + self.assertThat(spread_filter.search_bar.search_string, Eventually(Equals(""))) + + for w in cal_wins + char_wins: + self.assertWindowIsScaledEquals(w.x_id, True) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_switcher.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_switcher.py @@ -0,0 +1,747 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2010 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.display import move_mouse_to_screen +from autopilot.matchers import Eventually +from autopilot.testcase import multiply_scenarios +import logging +from testtools.matchers import Equals, Contains, Not +from time import sleep + +from unity.emulators.switcher import SwitcherDirection, SwitcherMode +from unity.tests import UnityTestCase + +logger = logging.getLogger(__name__) + +class SwitcherTestCase(UnityTestCase): + + scenarios = [ + ('show_desktop_icon_true', {'show_desktop_option': True}), + ('show_desktop_icon_false', {'show_desktop_option': False}), + ] + + def setUp(self): + super(SwitcherTestCase, self).setUp() + self.set_show_desktop(self.show_desktop_option) + + def set_show_desktop(self, state): + if type(state) is not bool: + raise TypeError("'state' must be boolean, not %r" % type(state)) + self.set_unity_option("disable_show_desktop", state) + self.assertThat(self.unity.switcher.show_desktop_disabled, Eventually(Equals(state))) + + def set_timeout_setting(self, state): + if type(state) is not bool: + raise TypeError("'state' must be boolean, not %r" % type(state)) + self.set_unity_option("alt_tab_timeout", state) + sleep(1) + + def start_applications(self, *args): + """Start some applications, returning their windows. + + If no applications are specified, the following will be started: + * Calculator + * Character Map + * Character Map + + Windows are always started in the order that they are specified (which + means the last specified application will *probably* be at the top of the + window stack after calling this method). Windows are returned in the same + order they are specified in. + + """ + if len(args) == 0: + args = ('Calculator', 'Character Map', 'Character Map') + windows = [] + for app in args: + windows.append(self.process_manager.start_app_window(app)) + + return windows + + +class SwitcherTests(SwitcherTestCase): + """Test the switcher.""" + + def setUp(self): + super(SwitcherTests, self).setUp() + self.set_timeout_setting(False) + + def tearDown(self): + super(SwitcherTests, self).tearDown() + + def test_switcher_starts_in_normal_mode(self): + """Switcher must start in normal (i.e.- not details) mode.""" + self.process_manager.start_app("Character Map") + + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + self.assertProperty(self.unity.switcher, mode=SwitcherMode.NORMAL) + + def test_label_matches_application_name(self): + """The switcher label must match the selected application name in normal mode.""" + windows = self.start_applications() + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + for win in windows: + app_name = win.application.name + self.unity.switcher.select_icon(SwitcherDirection.FORWARDS, tooltip_text=app_name) + self.assertThat(self.unity.switcher.label_visible, Eventually(Equals(True))) + self.assertThat(self.unity.switcher.label, Eventually(Equals(app_name))) + + def test_application_window_is_fake_decorated(self): + """When the switcher is in details mode must not show the focused window title.""" + window = self.process_manager.start_app_window("Text Editor") + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + self.unity.switcher.select_icon(SwitcherDirection.BACKWARDS, tooltip_text=window.application.name) + + self.unity.switcher.show_details() + self.assertThat(self.unity.switcher.label_visible, Eventually(Equals(False))) + self.assertThat(self.unity.screen.window(window.x_id).fake_decorated, Eventually(Equals(True))) + + def test_application_window_is_fake_decorated_in_detail_mode(self): + """Starting switcher in details mode must not show the focused window title.""" + window = self.process_manager.start_app_window("Text Editor") + self.unity.switcher.initiate(SwitcherMode.DETAIL) + self.addCleanup(self.unity.switcher.terminate) + + self.assertThat(self.unity.switcher.label_visible, Eventually(Equals(False))) + self.assertThat(self.unity.screen.window(window.x_id).fake_decorated, Eventually(Equals(True))) + + def test_switcher_move_next(self): + """Test that pressing the next icon binding moves to the next icon""" + self.start_applications() + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + start = self.unity.switcher.selection_index + self.unity.switcher.next_icon() + # Allow for wrap-around to first icon in switcher + next_index = (start + 1) % len(self.unity.switcher.icons) + + self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(next_index))) + + def test_switcher_move_prev(self): + """Test that pressing the previous icon binding moves to the previous icon""" + self.start_applications() + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + start = self.unity.switcher.selection_index + self.unity.switcher.previous_icon() + # Allow for wrap-around to last icon in switcher + prev_index = (start - 1) % len(self.unity.switcher.icons) + + self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(prev_index))) + + def test_switcher_scroll_next(self): + """Test that scrolling the mouse wheel down moves to the next icon""" + self.start_applications() + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + start = self.unity.switcher.selection_index + self.unity.switcher.next_via_mouse() + # Allow for wrap-around to first icon in switcher + next_index = (start + 1) % len(self.unity.switcher.icons) + + self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(next_index))) + + def test_switcher_scroll_prev(self): + """Test that scrolling the mouse wheel up moves to the previous icon""" + self.start_applications() + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + start = self.unity.switcher.selection_index + self.unity.switcher.previous_via_mouse() + # Allow for wrap-around to last icon in switcher + prev_index = (start - 1) % len(self.unity.switcher.icons) + + self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(prev_index))) + + def test_switcher_arrow_key_does_not_init(self): + """Ensure that Alt+Right does not initiate switcher. + + Regression test for LP:?????? + + """ + self.keyboard.press_and_release('Alt+Right') + self.assertThat(self.unity.switcher.visible, Equals(False)) + + def test_lazy_switcher_initiate(self): + """Inserting a long delay between the Alt press and the Tab tab must still + open the switcher. + + """ + self.process_manager.start_app("Character Map") + + self.keybinding_hold("switcher/reveal_normal") + self.addCleanup(self.keybinding_release, "switcher/reveal_normal") + self.assertThat(self.unity.switcher.visible, Eventually(Equals(False))) + sleep(5) + self.keybinding_tap("switcher/reveal_normal") + self.addCleanup(self.keybinding, "switcher/cancel") + self.assertThat(self.unity.switcher.visible, Eventually(Equals(True))) + + def test_switcher_cancel(self): + """Pressing the switcher cancel keystroke must cancel the switcher.""" + self.process_manager.start_app("Character Map") + + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + self.assertThat(self.unity.switcher.visible, Eventually(Equals(True))) + self.unity.switcher.cancel() + self.assertThat(self.unity.switcher.visible, Eventually(Equals(False))) + + def test_lazy_switcher_cancel(self): + """Must be able to cancel the switcher after a 'lazy' initiation.""" + self.process_manager.start_app("Character Map") + + self.keybinding_hold("switcher/reveal_normal") + self.addCleanup(self.keybinding_release, "switcher/reveal_normal") + self.assertThat(self.unity.switcher.visible, Eventually(Equals(False))) + sleep(5) + self.keybinding_tap("switcher/reveal_normal") + self.assertThat(self.unity.switcher.visible, Eventually(Equals(True))) + self.unity.switcher.cancel() + self.assertThat(self.unity.switcher.visible, Eventually(Equals(False))) + + def test_switcher_appears_on_monitor_with_mouse(self): + """Tests that the switches appears on the correct monitor. + + This is defined as the monitor with the mouse. + + """ + # TODO - this test fails in multi-monitor setups. You can't use addCleanup + # a better way would be to have a scenario'd class for multi-monitor + # switcher tests. + num_monitors = self.display.get_num_screens() + if num_monitors == 1: + self.skip("No point testing this on one monitor") + + charmap, calc, mahjongg = self.start_applications() + + self.addCleanup(self.unity.switcher.terminate) + + for monitor in range(num_monitors): + move_mouse_to_screen(monitor) + self.unity.switcher.initiate() + self.assertThat(self.unity.switcher.monitor, Eventually(Equals(monitor))) + self.unity.switcher.terminate() + sleep(1) + + def test_switcher_alt_f4_is_disabled(self): + """Tests that alt+f4 does not work while switcher is active.""" + + win = self.process_manager.start_app_window("Text Editor") + + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + self.assertThat(self.unity.switcher.visible, Eventually(Equals(True))) + + self.keyboard.press_and_release("Alt+F4") + # Need the sleep to allow the window time to close, for jenkins! + sleep(10) + self.assertProperty(win, is_valid=True) + + +class SwitcherWindowsManagementTests(SwitcherTestCase): + """Test the switcher window management.""" + + def setUp(self): + super(SwitcherTestCase, self).setUp() + + def tearDown(self): + super(SwitcherTestCase, self).tearDown() + + def test_switcher_raises_only_last_focused_window(self): + """Tests that when we do an alt+tab only the previously focused window is raised. + + This is tests by opening 2 Calculators and a Mahjongg. + Then we do a quick alt+tab twice. + Then we close the currently focused window. + + """ + char_win1, calc_win, char_win2 = self.start_applications("Character Map", "Calculator", "Character Map") + self.assertVisibleWindowStack([char_win2, calc_win, char_win1]) + + self.keybinding("switcher/reveal_normal") + self.assertProperty(calc_win, is_focused=True) + self.assertVisibleWindowStack([calc_win, char_win2, char_win1]) + + self.keybinding("switcher/reveal_normal") + self.assertProperty(char_win2, is_focused=True) + self.assertVisibleWindowStack([char_win2, calc_win, char_win1]) + + self.keybinding("window/close") + self.assertProperty(calc_win, is_focused=True) + self.assertVisibleWindowStack([calc_win, char_win1]) + + def test_switcher_rises_next_window_of_same_application(self): + """Tests if alt+tab invoked normally switches to the next application + window of the same type. + + """ + char_win1, char_win2 = self.start_applications("Character Map", "Character Map") + self.assertVisibleWindowStack([char_win2, char_win1]) + + self.keybinding("switcher/reveal_normal") + self.assertProperty(char_win1, is_focused=True) + + def test_switcher_rises_other_application(self): + """Tests if alt+tab invoked normally switches correctly to the other + application window when the last focused application had 2 windows + + """ + char_win1, char_win2, calc_win = self.start_applications("Character Map", "Character Map", "Calculator") + self.assertVisibleWindowStack([calc_win, char_win2, char_win1]) + + self.keybinding("switcher/reveal_normal") + self.assertProperty(char_win2, is_focused=True) + + self.keybinding("switcher/reveal_normal") + self.assertProperty(calc_win, is_focused=True) + + +class SwitcherInteractionTests(SwitcherTestCase): + """Test the switcher interactions with the rest of the shell.""" + + def setUp(self): + super(SwitcherTestCase, self).setUp() + + def tearDown(self): + super(SwitcherTestCase, self).tearDown() + + def open_switcher_after_overlay(self, overlay): + self.start_applications() + self.addCleanup(overlay.ensure_hidden) + overlay.ensure_visible() + + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + self.assertThat(overlay.visible, Eventually(Equals(False))) + self.assertThat(self.unity.switcher.visible, Eventually(Equals(True))) + + +class SwitcherOverlaysInteractionTests(SwitcherInteractionTests): + """Test the switcher interactions with the shell overlays.""" + + scenarios = multiply_scenarios(SwitcherTestCase.scenarios, + [ + ('Dash', {'overlay': "self.unity.dash"}), + ('Hud', {'overlay': "self.unity.hud"}), + ] + ) + + def setUp(self): + super(SwitcherOverlaysInteractionTests, self).setUp() + self.overlay = eval(self.overlay) + + def tearDown(self): + super(SwitcherOverlaysInteractionTests, self).tearDown() + + def test_switcher_shows_on_overlay_opened(self): + """Tests if switcher shows when overlay is opened""" + self.open_switcher_after_overlay(self.overlay) + + def test_switcher_tab_key_work_after_overlay_is_closed(self): + """Tests that the switcher tab key work when initializing the + switcher after closing the overlay + """ + self.open_switcher_after_overlay(self.overlay) + + start = self.unity.switcher.selection_index + next_index = (start + 1) % len(self.unity.switcher.icons) + self.unity.switcher.next_icon() + self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(next_index))) + + self.unity.switcher.previous_icon() + self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(start))) + + def test_switcher_arrow_keys_work_after_overlay_is_closed(self): + """Tests that the switcher arrow keys work when initializing the + switcher after closing the overlay + """ + self.open_switcher_after_overlay(self.overlay) + + start = self.unity.switcher.selection_index + next_index = (start + 1) % len(self.unity.switcher.icons) + self.keyboard.press_and_release('Right') + self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(next_index))) + + self.keyboard.press_and_release('Left') + self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(start))) + + def test_switcher_detail_mode_works_after_overlay_is_closed(self): + """Tests that the switcher detail mode through the 'Down' arrow key + work when initializing the switcher after closing the overlay + """ + self.open_switcher_after_overlay(self.overlay) + + self.keyboard.press_and_release('Down') + self.assertProperty(self.unity.switcher, mode=SwitcherMode.DETAIL) + + self.keyboard.press_and_release('Up') + self.assertProperty(self.unity.switcher, mode=SwitcherMode.NORMAL) + + +class SwitcherDetailsTests(SwitcherTestCase): + """Test the details mode for the switcher.""" + + def setUp(self): + super(SwitcherDetailsTests, self).setUp() + self.set_timeout_setting(True) + + def test_details_mode_on_delay(self): + """Test that details mode activates on a timeout.""" + if self.workspace.num_workspaces <= 1: + self.skipTest("This test requires enabled more than one workspace.") + initial_workspace = self.workspace.current_workspace + self.addCleanup(self.workspace.switch_to, initial_workspace) + self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) + self.start_applications("Character Map", "Character Map", "Mahjongg") + + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + # Wait longer than details mode. + sleep(3) + self.assertProperty(self.unity.switcher, mode=SwitcherMode.DETAIL) + + def test_no_details_for_apps_on_different_workspace(self): + """Tests that details mode does not initiates when there are multiple windows + + of an application spread across different workspaces. + Regression test for LP:933406. + + """ + if self.workspace.num_workspaces <= 1: + self.skipTest("This test requires enabled more than one workspace.") + initial_workspace = self.workspace.current_workspace + self.addCleanup(self.workspace.switch_to, initial_workspace) + self.process_manager.start_app_window("Character Map") + self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) + self.start_applications("Character Map", "Mahjongg") + + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + # Wait longer than details mode. + sleep(3) + self.assertProperty(self.unity.switcher, mode=SwitcherMode.NORMAL) + + +class SwitcherDetailsModeTests(SwitcherTestCase): + """Tests for the details mode of the switcher. + + Tests for initiation with both grave (`) and Down arrow. + + """ + + scenarios = multiply_scenarios(SwitcherTestCase.scenarios, + [ + ('initiate_with_grave', {'initiate_keycode': '`'}), + ('initiate_with_down', {'initiate_keycode': 'Down'}), + ] + ) + + def setUp(self): + super(SwitcherDetailsModeTests, self).setUp() + self.set_timeout_setting(False) + + def test_can_start_details_mode(self): + """Must be able to switch to details mode using selected scenario keycode. + + """ + self.process_manager.start_app_window("Character Map") + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + self.keyboard.press_and_release(self.initiate_keycode) + + self.assertProperty(self.unity.switcher, mode=SwitcherMode.DETAIL) + + def test_next_icon_from_last_detail_works(self): + """Pressing next while showing last switcher item in details mode + must select first item in the model in non-details mode. + + """ + self.process_manager.start_app("Character Map") + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + while self.unity.switcher.selection_index < len(self.unity.switcher.icons) - 1: + self.unity.switcher.next_icon() + self.keyboard.press_and_release(self.initiate_keycode) + sleep(0.5) + # Make sure we're at the end of the details list for this icon + possible_details = self.unity.switcher.detail_current_count - 1 + while self.unity.switcher.detail_selection_index < possible_details: + self.unity.switcher.next_detail() + + self.unity.switcher.next_icon() + self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(0))) + + def test_detail_mode_selects_last_active_window(self): + """The active selection in detail mode must be the last focused window. + If it was the currently active application type. + """ + char_win1, char_win2 = self.start_applications("Character Map", "Character Map") + self.assertVisibleWindowStack([char_win2, char_win1]) + + self.unity.switcher.initiate() + while self.unity.switcher.current_icon.tooltip_text != char_win2.application.name: + self.unity.switcher.next_icon() + self.keyboard.press_and_release(self.initiate_keycode) + sleep(0.5) + self.unity.switcher.select() + + self.assertProperty(char_win1, is_focused=True) + + def test_detail_mode_selects_third_window(self): + """Pressing Alt+` twice must select the third last used window. + LP:1061229 + """ + char_win1, char_win2, char_win3 = self.start_applications("Character Map", "Character Map", "Character Map") + self.assertVisibleWindowStack([char_win3, char_win2, char_win1]) + + self.unity.switcher.initiate(SwitcherMode.DETAIL) + self.unity.switcher.next_detail() + + self.unity.switcher.select() + self.assertVisibleWindowStack([char_win1, char_win3, char_win2]) + + +class SwitcherWorkspaceTests(SwitcherTestCase): + """Test Switcher behavior with respect to multiple workspaces.""" + + def setUp(self): + super(SwitcherWorkspaceTests, self).setUp() + if self.workspace.num_workspaces <= 1: + self.skipTest("This test requires enabled more than one workspace.") + + def test_switcher_shows_current_workspace_only(self): + """Switcher must show apps from the current workspace only.""" + initial_workspace = self.workspace.current_workspace + self.addCleanup(self.workspace.switch_to, initial_workspace) + + calc = self.process_manager.start_app("Calculator") + self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) + char_map = self.process_manager.start_app("Character Map") + + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + get_icon_names = lambda: [i.tooltip_text for i in self.unity.switcher.icons] + self.assertThat(get_icon_names, Eventually(Contains(char_map.name))) + self.assertThat(get_icon_names, Eventually(Not(Contains(calc.name)))) + + def test_switcher_switch_current_workspace_same_apps_diff_workspace(self): + initial_workspace = self.workspace.current_workspace + self.addCleanup(self.workspace.switch_to, initial_workspace) + + char_map = self.process_manager.start_app_window("Character Map") + calc1 = self.process_manager.start_app_window("Calculator") + + self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) + + calc2 = self.process_manager.start_app_window("Calculator") + + self.workspace.switch_to(initial_workspace); + + self.unity.switcher.initiate() + self.unity.switcher.select() + + self.assertProperty(char_map, is_focused=True) + + def test_switcher_all_mode_shows_all_apps(self): + """Test switcher 'show_all' mode shows apps from all workspaces.""" + initial_workspace = self.workspace.current_workspace + self.addCleanup(self.workspace.switch_to, initial_workspace) + + calc = self.process_manager.start_app("Calculator") + self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) + char_map = self.process_manager.start_app("Character Map") + + self.unity.switcher.initiate(SwitcherMode.ALL) + self.addCleanup(self.unity.switcher.terminate) + + get_icon_names = lambda: [i.tooltip_text for i in self.unity.switcher.icons] + self.assertThat(get_icon_names, Eventually(Contains(calc.name))) + self.assertThat(get_icon_names, Eventually(Contains(char_map.name))) + + def test_switcher_can_switch_to_minimised_window(self): + """Switcher must be able to switch to a minimised window when there's + + another instance of the same application on a different workspace. + + """ + initial_workspace = self.workspace.current_workspace + self.addCleanup(self.workspace.switch_to, initial_workspace) + + # disable automatic gridding of the switcher after a timeout, since it makes + # it harder to write the tests. + self.set_unity_option("alt_tab_timeout", False) + + self.process_manager.start_app("Character Map") + + self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) + char_win2 = self.process_manager.start_app_window("Character Map") + self.keybinding("window/minimize") + self.assertProperty(char_win2, is_hidden=True) + + self.process_manager.start_app("Calculator") + + self.unity.switcher.initiate() + while self.unity.switcher.current_icon.tooltip_text != char_win2.application.name: + self.unity.switcher.next_icon() + self.unity.switcher.select() + + self.assertProperty(char_win2, is_hidden=False) + + def test_switcher_is_disabled_when_wall_plugin_active(self): + """The switcher must not open when the wall plugin is active using ctrl+alt+.""" + initial_workspace = self.workspace.current_workspace + self.addCleanup(self.workspace.switch_to, initial_workspace) + + self.workspace.switch_to(0) + sleep(1) + self.keyboard.press("Ctrl+Alt+Right") + self.addCleanup(self.keyboard.release, "Ctrl+Alt+Right") + sleep(1) + self.keybinding_hold_part_then_tap("switcher/reveal_normal") + self.addCleanup(self.unity.switcher.terminate) + + self.assertThat(self.unity.switcher.visible, Eventually(Equals(False))) + +class SwitcherDetailsMouseTests(SwitcherTestCase): + """ Test the interactions with the mouse and the switcher. """ + + def setUp(self): + super(SwitcherDetailsMouseTests, self).setUp() + self.set_timeout_setting(False) + self.mouse.move(0, 0, animate=False) + + def test_mouse_highlights_switcher_icons(self): + """ Tests that the mouse can hightlight all the switcher icons. """ + + self.process_manager.start_app("Character Map") + + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + icon_args = self.unity.switcher.view.icon_args + offset = self.unity.switcher.view.spread_offset + icon_cords = [] + + # Must collect the cords before moving mouse + for args in icon_args: + x = args.logical_center.x + offset + y = args.logical_center.y + offset + icon_cords.append((x,y)) + + self.unity.switcher.view.break_mouse_bump_detection() + + index = 0; + for cords in icon_cords: + self.mouse.move(cords[0], cords[1]) + self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(index))) + index += 1 + + def test_mouse_clicks_activate_icon(self): + """ + Opens 2 different applications, CharMap being opened before TextEditor. + Then we get the index of the CharMap, and click on it, asserting CharMap is focused. + """ + + char_win1, char_win2 = self.start_applications("Character Map", "Text Editor") + self.assertVisibleWindowStack([char_win2, char_win1]) + self.assertProperty(char_win1, is_focused=False) + + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + index = self.unity.switcher.selection_index + self.unity.switcher.view.move_over_icon(index); + self.mouse.click() + + self.assertProperty(char_win1, is_focused=True) + + def test_mouse_doesnt_hightlight_icon_if_over_on_start(self): + """ + First start the launcher and move the mosue over position of Text Editor icon, + then close the switcher and open it again while moving the mouse a bit. + Asserting that the icon does lose focus from Character Map. + """ + + char_win1, char_win2 = self.start_applications("Character Map", "Text Editor") + self.assertVisibleWindowStack([char_win2, char_win1]) + self.assertProperty(char_win1, is_focused=False) + + self.unity.switcher.initiate() + self.addCleanup(self.unity.switcher.terminate) + + mouse_index = self.unity.switcher.selection_index - 1 + + self.unity.switcher.view.move_over_icon(mouse_index) + # Assert we are over the icon we want to hover over. + self.assertThat(self.unity.switcher.view.last_icon_selected, Eventually(Equals(mouse_index))) + + self.addCleanup(self.keybinding, "switcher/cancel") + + self.unity.switcher.terminate() + self.unity.switcher.initiate() + + index = self.unity.switcher.selection_index + + pos = self.mouse.position() + self.mouse.move(pos[0] + 5, pos[1] + 5) + + # Assert moving the mouse does not change the selection + self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(index))) + + # Also nice to know clicking still works, even without selection + self.mouse.click() + + self.assertProperty(char_win2, is_focused=True) + + def test_mouse_highlights_switcher_deatil_icons_motion(self): + """ + Gather the cords of all the detail icons, move the mouse through each + asserting the index of each icon we move through. + """ + + self.start_applications("Character Map", "Character Map", "Character Map") + + self.unity.switcher.initiate(SwitcherMode.DETAIL) + self.addCleanup(self.unity.switcher.terminate) + + for index in range(len(self.unity.switcher.view.detail_icons)): + self.unity.switcher.view.move_over_detail_icon(index) + self.assertThat(self.unity.switcher.detail_selection_index, Eventually(Equals(index))) + + def test_mouse_click_will_activate_detail_icon(self): + """ + Start 2 application of the same type, then click on index 0 in detail mode. This + will cause the focus from char_win2 to move to char_win1, showing clicking wokrs. + """ + + char_win1, char_win2 = self.start_applications("Character Map", "Character Map") + self.assertVisibleWindowStack([char_win2, char_win1]) + + self.unity.switcher.initiate(SwitcherMode.DETAIL) + self.addCleanup(self.unity.switcher.terminate) + + self.unity.switcher.view.move_over_detail_icon(0); + self.mouse.click() + + self.assertProperty(char_win1, is_focused=True) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_unity_logging.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_unity_logging.py @@ -0,0 +1,69 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Thomi Richards +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from os import remove +from os.path import exists +from tempfile import mktemp +from testtools.matchers import Contains, Not +from time import sleep + + +from unity.emulators.unity import ( + start_log_to_file, + reset_logging, + set_log_severity, + log_unity_message, + ) +from unity.tests import UnityTestCase + + +class UnityLoggingTests(UnityTestCase): + """Tests for Unity's debug logging framework.""" + + def start_new_log_file(self): + fpath = mktemp() + start_log_to_file(fpath) + return fpath + + def test_new_file_created(self): + """Unity must create log file when we call start_log_to_file. + """ + fpath = self.start_new_log_file() + self.addCleanup(remove, fpath) + self.addCleanup(reset_logging) + sleep(1) + self.assertTrue(exists(fpath)) + + def test_messages_arrive_in_file(self): + fpath = self.start_new_log_file() + log_unity_message("WARNING", "This is a warning of things to come") + sleep(1) + reset_logging() + + with open(fpath, 'r') as f: + self.assertThat(f.read(), Contains("This is a warning of things to come")) + + def test_default_log_level_unchanged(self): + fpath = self.start_new_log_file() + log_unity_message("DEBUG", "This is some INFORMATION") + sleep(1) + reset_logging() + with open(fpath, 'r') as f: + self.assertThat(f.read(), Not(Contains("This is some INFORMATION"))) + + def test_can_change_log_level(self): + fpath = self.start_new_log_file() + set_log_severity("", "DEBUG") + self.addCleanup(set_log_severity, "", "INFO") + log_unity_message("DEBUG", "This is some more INFORMATION") + sleep(1) + reset_logging() + with open(fpath, 'r') as f: + self.assertThat(f.read(), Contains("This is some more INFORMATION")) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_wm_keybindings.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/test_wm_keybindings.py @@ -0,0 +1,166 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2013 Canonical +# Author: Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +from testtools.matchers import Equals, NotEquals, GreaterThan +from unity.tests import UnityTestCase +from unity.emulators import switcher + +class WindowManagerKeybindings(UnityTestCase): + """Window Manager keybindings tests""" + + def setUp(self): + super(WindowManagerKeybindings, self).setUp() + + def open_panel_menu(self): + panel = self.unity.panels.get_panel_for_monitor(0) + self.assertThat(lambda: len(panel.get_indicator_entries()), Eventually(GreaterThan(0))) + self.addCleanup(self.keyboard.press_and_release, "Escape") + self.keybinding("panel/open_first_menu") + self.assertThat(self.unity.panels.get_active_indicator, Eventually(NotEquals(None))) + + def test_dash_shows_on_menus_opened(self): + self.open_panel_menu() + self.addCleanup(self.unity.dash.ensure_hidden) + self.unity.dash.ensure_visible() + self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) + + def test_hud_shows_on_menus_opened(self): + self.open_panel_menu() + self.addCleanup(self.unity.hud.ensure_hidden) + self.unity.hud.ensure_visible() + self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) + + def test_switcher_shows_on_menus_opened(self): + self.open_panel_menu() + self.addCleanup(self.unity.switcher.terminate) + self.unity.switcher.initiate() + self.assertProperty(self.unity.switcher, mode=switcher.SwitcherMode.NORMAL) + self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) + + def test_shortcut_hints_shows_on_menus_opened(self): + self.open_panel_menu() + self.addCleanup(self.unity.shortcut_hint.ensure_hidden) + self.unity.shortcut_hint.show() + self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(True))) + self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) + + def test_spread_shows_on_menus_opened(self): + self.open_panel_menu() + self.addCleanup(self.unity.window_manager.terminate_spread) + self.unity.window_manager.initiate_spread() + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) + self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) + + +class WindowManagerKeybindingsForWindowHandling(UnityTestCase): + """Window Manager keybindings tests for handling a window""" + + scenarios = [('Restored Window', {'start_restored': True}), + ('Maximized Window', {'start_restored': False})] + + def setUp(self): + super(WindowManagerKeybindingsForWindowHandling, self).setUp() + self.start_test_window() + + def keybinding_if_not_minimized(self, keybinding): + if not self.screen_win.minimized: + self.keybinding(keybinding) + + def keybinding_if_not_restored(self, keybinding): + if self.screen_win.vertically_maximized or self.screen_win.horizontally_maximized: + self.keybinding(keybinding) + + def start_test_window(self, app_name="Character Map"): + """Start a restored/maximized window of the requested application""" + self.process_manager.close_all_app(app_name) + self.bamf_win = self.process_manager.start_app_window(app_name) + win_fn = lambda: self.unity.screen.window(self.bamf_win.x_id) + self.assertThat(win_fn, Eventually(NotEquals(None))) + self.screen_win = win_fn() + + if self.start_restored: + if self.screen_win.vertically_maximized or self.screen_win.horizontally_maximized: + self.addCleanup(self.keybinding_if_not_minimized, "window/maximize") + self.keybinding("window/restore") + else: + if not self.screen_win.vertically_maximized and not self.screen_win.horizontally_maximized: + self.addCleanup(self.keybinding_if_not_restored, "window/restore") + self.keybinding("window/maximize") + + def get_window_workarea(self): + monitor = self.bamf_win.monitor + monitor_geo = self.display.get_screen_geometry(monitor) + launcher = self.unity.launcher.get_launcher_for_monitor(monitor) + launcher_w = 0 if launcher.hidemode else launcher.geometry[2] + panel_h = self.unity.panels.get_panel_for_monitor(monitor).geometry[3] + return (monitor_geo[0] + launcher_w, monitor_geo[1] + panel_h, + monitor_geo[2] - launcher_w, monitor_geo[3] - panel_h) + + def test_maximize_window(self): + if self.start_restored: + self.addCleanup(self.keybinding, "window/restore") + self.keybinding("window/maximize") + self.assertThat(self.screen_win.maximized, Eventually(Equals(True))) + + def test_restore_maximized_window(self): + if self.start_restored: + self.keybinding("window/maximize") + self.keybinding("window/restore") + self.assertThat(self.screen_win.maximized, Eventually(Equals(False))) + self.assertThat(self.screen_win.minimized, Eventually(Equals(False))) + + def test_restore_vertically_maximized_window(self): + if not self.start_restored: + self.addCleanup(self.keybinding, "window/maximize") + self.keybinding("window/restore") + (x1, y1, w1, h1) = self.screen_win.geometry + self.keyboard.press_and_release("Ctrl+Super+Right") + self.keybinding("window/restore") + (x2, y2, w2, h2) = self.screen_win.geometry + + self.assertThat(self.screen_win.vertically_maximized, Eventually(Equals(False))) + self.assertThat(self.screen_win.minimized, Eventually(Equals(False))) + # Make sure the window restored to its same geometry before vertically maximizing + self.assertThat(x1, Equals(x2)) + self.assertThat(y1, Equals(y2)) + self.assertThat(w1, Equals(w2)) + self.assertThat(h1, Equals(h2)) + + def test_minimize_restored_window(self): + if not self.start_restored: + self.addCleanup(self.keybinding_if_not_minimized, "window/maximize") + self.keybinding("window/restore") + self.keybinding("window/restore") + self.assertThat(self.screen_win.minimized, Eventually(Equals(True))) + + def test_left_maximize(self): + self.addCleanup(self.keybinding, "window/restore" if self.start_restored else "window/maximize") + self.keyboard.press_and_release("Ctrl+Super+Left") + self.assertThat(self.screen_win.vertically_maximized, Eventually(Equals(True))) + self.assertThat(self.screen_win.horizontally_maximized, Eventually(Equals(False))) + + workarea_geo = self.get_window_workarea() + self.assertThat(self.screen_win.x, Eventually(Equals(workarea_geo[0]))) + self.assertThat(self.screen_win.y, Eventually(Equals(workarea_geo[1]))) + self.assertThat(self.screen_win.width, Eventually(Equals(int(workarea_geo[2]/2.0 + 0.5)))) + self.assertThat(self.screen_win.height, Eventually(Equals(workarea_geo[3]))) + + def test_right_maximize(self): + self.addCleanup(self.keybinding, "window/restore" if self.start_restored else "window/maximize") + self.keyboard.press_and_release("Ctrl+Super+Right") + self.assertThat(self.screen_win.vertically_maximized, Eventually(Equals(True))) + self.assertThat(self.screen_win.horizontally_maximized, Eventually(Equals(False))) + + workarea_geo = self.get_window_workarea() + self.assertThat(self.screen_win.x, Eventually(Equals(workarea_geo[0]+workarea_geo[2]/2))) + self.assertThat(self.screen_win.y, Eventually(Equals(workarea_geo[1]))) + self.assertThat(self.screen_win.width, Eventually(Equals(int(workarea_geo[2]/2.0 + 0.5)))) + self.assertThat(self.screen_win.height, Eventually(Equals(workarea_geo[3]))) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/xim/test_gcin.py +++ unity-7.4.0+16.04.20151218/tests/autopilot/build/lib.linux-x86_64-2.7/unity/tests/xim/test_gcin.py @@ -0,0 +1,68 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Brandon Schaefer +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +from os import environ +import subprocess +from testtools.matchers import Equals +from testtools import skip +from unity.tests import UnityTestCase + + +class GcinTestCase(UnityTestCase): + """Tests the Input Method gcin.""" + + def setUp(self): + super(GcinTestCase, self).setUp() + + # Check that gcin is set as the active IM through im-switch + if environ.get('XMODIFIERS', '') != "@im=gcin": + self.skip("Please make sure XMODIFIERS is set to @im=gcin. Set it using 'im-switch'.") + + running_process = subprocess.check_output('ps -e', shell=True) + if 'gcin' not in running_process: + self.skip("gcin is not an active process, please start 'gcin' before running these tests.") + +class GcinTestHangul(GcinTestCase): + """Tests the Dash and Hud with gcin in hangul mode.""" + + scenarios = [ + ('hangul', {'input': 'han geul ', 'result': u'\ud55c\uae00'}), + ('morning letters', {'input': 'a chimgeul ', 'result': u'\uc544\uce68\uae00'}), + ('national script', {'input': 'gug mun ', 'result': u'\uad6d\ubb38'}), + ] + + def setUp(self): + super(GcinTestHangul, self).setUp() + + def enter_hangul_mode(self): + """Ctrl+Space turns gcin on, Ctrl+Alt+/ turns hangul on.""" + self.keyboard.press_and_release("Ctrl+Space") + self.keyboard.press_and_release("Ctrl+Alt+/") + + def test_dash_input(self): + """Entering an input string through gcin will result in a Korean string result in the dash.""" + + self.unity.dash.ensure_visible() + self.addCleanup(self.unity.dash.ensure_hidden) + self.enter_hangul_mode() + + self.keyboard.type(self.input) + self.assertThat(self.unity.dash.search_string, Eventually(Equals(self.result))) + + def test_hud_input(self): + """Entering an input string through gcin will result in a Korean string result in the hud.""" + + self.unity.hud.ensure_visible() + self.addCleanup(self.unity.hud.ensure_hidden) + self.enter_hangul_mode() + + self.keyboard.type(self.input) + self.assertThat(self.unity.hud.search_string, Eventually(Equals(self.result))) only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/unity.egg-info/PKG-INFO +++ unity-7.4.0+16.04.20151218/tests/autopilot/unity.egg-info/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: unity +Version: 1.0 +Summary: Unity autopilot tests. +Home-page: https://launchpad.net/unity +Author: Alex Launi +Author-email: alex.launi@canonical.com +License: GPLv3 +Description: UNKNOWN +Platform: UNKNOWN only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/unity.egg-info/SOURCES.txt +++ unity-7.4.0+16.04.20151218/tests/autopilot/unity.egg-info/SOURCES.txt @@ -0,0 +1,54 @@ +README +setup.py +unity/__init__.py +unity.egg-info/PKG-INFO +unity.egg-info/SOURCES.txt +unity.egg-info/dependency_links.txt +unity.egg-info/top_level.txt +unity/emulators/X11.py +unity/emulators/__init__.py +unity/emulators/compiz.py +unity/emulators/dash.py +unity/emulators/hud.py +unity/emulators/ibus.py +unity/emulators/icons.py +unity/emulators/launcher.py +unity/emulators/panel.py +unity/emulators/quicklist.py +unity/emulators/screen.py +unity/emulators/shortcut_hint.py +unity/emulators/switcher.py +unity/emulators/tooltip.py +unity/emulators/unity.py +unity/emulators/window_manager.py +unity/emulators/workspace.py +unity/tests/__init__.py +unity/tests/test_command_lens.py +unity/tests/test_dash.py +unity/tests/test_gnome_key_grabber.py +unity/tests/test_gobject_introspection.py +unity/tests/test_home_lens.py +unity/tests/test_hud.py +unity/tests/test_ibus.py +unity/tests/test_panel.py +unity/tests/test_quicklist.py +unity/tests/test_search.py +unity/tests/test_shopping_lens.py +unity/tests/test_shortcut_hint.py +unity/tests/test_showdesktop.py +unity/tests/test_spread.py +unity/tests/test_switcher.py +unity/tests/test_unity_logging.py +unity/tests/test_wm_keybindings.py +unity/tests/launcher/__init__.py +unity/tests/launcher/test_capture.py +unity/tests/launcher/test_icon_behavior.py +unity/tests/launcher/test_keynav.py +unity/tests/launcher/test_reveal.py +unity/tests/launcher/test_scroll.py +unity/tests/launcher/test_shortcut.py +unity/tests/launcher/test_switcher.py +unity/tests/launcher/test_tooltips.py +unity/tests/launcher/test_visual.py +unity/tests/xim/__init__.py +unity/tests/xim/test_gcin.py \ No newline at end of file only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/unity.egg-info/dependency_links.txt +++ unity-7.4.0+16.04.20151218/tests/autopilot/unity.egg-info/dependency_links.txt @@ -0,0 +1 @@ + only in patch2: unchanged: --- unity-7.4.0+16.04.20151218.orig/tests/autopilot/unity.egg-info/top_level.txt +++ unity-7.4.0+16.04.20151218/tests/autopilot/unity.egg-info/top_level.txt @@ -0,0 +1 @@ +unity