summaryrefslogtreecommitdiff
diff options
authorAndrea Azzarone <azzaronea@gmail.com>2012-02-06 13:24:56 +0100
committerAndrea Azzarone <azzaronea@gmail.com>2012-02-06 13:24:56 +0100
commit601ef902870aee6fccd89f5a134a30ba9a7b6699 (patch)
tree58528d1bebb2f3eb403b881f4478e2821e0b179c
parent46016b9f6d6beee92d429ef7b049029b575bbc87 (diff)
parentfeae5b47ffb88b8d49d4edd79220bf79ce6a4d6a (diff)
Merge thomi's branch.
(bzr r1899.2.4)
-rw-r--r--manual-tests/Showdesktop.txt15
-rw-r--r--plugins/unityshell/src/DashView.cpp1
-rw-r--r--plugins/unityshell/src/Introspectable.h9
-rw-r--r--plugins/unityshell/src/Launcher.cpp3
-rw-r--r--plugins/unityshell/src/LensBar.cpp10
-rw-r--r--plugins/unityshell/src/SwitcherModel.cpp18
-rw-r--r--tests/autopilot/autopilot/emulators/X11.py234
-rw-r--r--tests/autopilot/autopilot/emulators/bamf.py341
-rw-r--r--tests/autopilot/autopilot/emulators/unity.py278
-rw-r--r--tests/autopilot/autopilot/tests/__init__.py13
-rw-r--r--tests/autopilot/autopilot/tests/test_dash.py57
-rw-r--r--tests/autopilot/autopilot/tests/test_launcher.py21
-rw-r--r--tests/autopilot/autopilot/tests/test_showdesktop.py138
-rw-r--r--tests/autopilot/autopilot/tests/test_switcher.py48
14 files changed, 897 insertions, 289 deletions
diff --git a/manual-tests/Showdesktop.txt b/manual-tests/Showdesktop.txt
deleted file mode 100644
index e06ca4704..000000000
--- a/manual-tests/Showdesktop.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Test Showdesktop Mode
--------------------
-This test shows that the "show desktop" mode works correctly
-
-#. Open two applications
-#. Use either alt-tab or ctrl-alt-d to activate "show desktop" mode
-#. Use either alt-tab or ctrl-alt-d to deactivate "show desktop" mode
-#. Use either alt-tab or ctrl-alt-d to activate "show desktop" mode
-#. Select an active application from the launcher
-#. Use either alt-tab or ctrl-alt-d to deactivate "show desktop" mode
-
-Outcome
- Both windows will fade out, both windows will fade in, both windows
- will fade out, the clicked application will fade in only, all other
- windows will fade in.
diff --git a/plugins/unityshell/src/DashView.cpp b/plugins/unityshell/src/DashView.cpp
index f62edca90..4a15c814a 100644
--- a/plugins/unityshell/src/DashView.cpp
+++ b/plugins/unityshell/src/DashView.cpp
@@ -154,6 +154,7 @@ void DashView::SetupViews()
lenses_layout_->AddView(home_view_);
lens_bar_ = new LensBar();
+ AddChild(lens_bar_);
lens_bar_->lens_activated.connect(sigc::mem_fun(this, &DashView::OnLensBarActivated));
content_layout_->AddView(lens_bar_, 0, nux::MINOR_POSITION_CENTER);
}
diff --git a/plugins/unityshell/src/Introspectable.h b/plugins/unityshell/src/Introspectable.h
index d7a5022fd..baa40fde9 100644
--- a/plugins/unityshell/src/Introspectable.h
+++ b/plugins/unityshell/src/Introspectable.h
@@ -52,13 +52,8 @@ protected:
* AddProperties should be implemented as such ...
* void ClassFoo::AddProperties (GVariantBuilder *builder)
* {
- * g_variant_builder_add (builder, "{sv}", "label", g_variant_new_string ("_File") );
- * g_variant_builder_add (builder, "{sv}", "image", g_variant_new_string ("") );
- * g_variant_builder_add (builder, "{sv}", "visible", g_variant_new_boolean (TRUE) );
- * g_variant_builder_add (builder, "{sv}", "sensitive", g_variant_new_boolean (TRUE) );
- * g_variant_builder_add (builder, "{sv}", "active", g_variant_new_boolean (FALSE) );
- * g_variant_builder_add (builder, "{sv}", "label", g_variant_new_int32 (34) );
- * g_variant_builder_add (builder, "{sv}", "label", g_variant_new_int32 (24) );
+ * unity::variant::BuilderWrapper wrapper(builder);
+ * wrapper.add("label", some_value);
* }
* That's all. Just add a bunch of key-value properties to the builder.
*/
diff --git a/plugins/unityshell/src/Launcher.cpp b/plugins/unityshell/src/Launcher.cpp
index 1fc19b935..70b79f1ae 100644
--- a/plugins/unityshell/src/Launcher.cpp
+++ b/plugins/unityshell/src/Launcher.cpp
@@ -411,7 +411,8 @@ Launcher::AddProperties(GVariantBuilder* builder)
.add("monitor", monitor())
.add("quicklist-open", _hide_machine->GetQuirk(LauncherHideMachine::QUICKLIST_OPEN))
.add("hide-quirks", _hide_machine->DebugHideQuirks().c_str())
- .add("hover-quirks", _hover_machine->DebugHoverQuirks().c_str());
+ .add("hover-quirks", _hover_machine->DebugHoverQuirks().c_str())
+ .add("icon-size", _icon_size);
}
void Launcher::SetMousePosition(int x, int y)
diff --git a/plugins/unityshell/src/LensBar.cpp b/plugins/unityshell/src/LensBar.cpp
index 15d13fc54..e3bb77861 100644
--- a/plugins/unityshell/src/LensBar.cpp
+++ b/plugins/unityshell/src/LensBar.cpp
@@ -253,7 +253,15 @@ std::string LensBar::GetName() const
}
void LensBar::AddProperties(GVariantBuilder* builder)
-{}
+{
+ unity::variant::BuilderWrapper wrapper(builder);
+ for( auto icon : icons_)
+ {
+ if (icon->active)
+ wrapper.add("active-lens", icon->id.Get());
+ }
+
+}
}
}
diff --git a/plugins/unityshell/src/SwitcherModel.cpp b/plugins/unityshell/src/SwitcherModel.cpp
index 9141ea509..37f63991f 100644
--- a/plugins/unityshell/src/SwitcherModel.cpp
+++ b/plugins/unityshell/src/SwitcherModel.cpp
@@ -40,13 +40,19 @@ SwitcherModel::SwitcherModel(std::vector<AbstractLauncherIcon*> icons)
only_detail_on_viewport = false;
for (auto icon : _inner)
+ {
+ AddChild(icon);
icon->Reference();
+ }
}
SwitcherModel::~SwitcherModel()
{
for (auto icon : _inner)
+ {
+ RemoveChild(icon);
icon->UnReference();
+ }
}
std::string SwitcherModel::GetName() const
@@ -56,22 +62,12 @@ std::string SwitcherModel::GetName() const
void SwitcherModel::AddProperties(GVariantBuilder* builder)
{
- GVariantBuilder children_builder;
- g_variant_builder_init(&children_builder, G_VARIANT_TYPE("a(sv)"));
-
- for (auto icon : _inner)
- {
- if (icon->GetName() != "")
- g_variant_builder_add(&children_builder, "(sv)", icon->GetName().c_str(), icon->Introspect());
- }
-
unity::variant::BuilderWrapper(builder)
.add("detail-selection", detail_selection)
.add("detail-selection-index", (int)detail_selection_index)
.add("only-detail-on-viewport", only_detail_on_viewport)
.add("selection-index", SelectionIndex())
- .add("last-selection-index", LastSelectionIndex())
- .add("children-of-men", g_variant_builder_end(&children_builder));
+ .add("last-selection-index", LastSelectionIndex());
}
SwitcherModel::iterator
diff --git a/tests/autopilot/autopilot/emulators/X11.py b/tests/autopilot/autopilot/emulators/X11.py
index 8ee7c0d65..a668d529c 100644
--- a/tests/autopilot/autopilot/emulators/X11.py
+++ b/tests/autopilot/autopilot/emulators/X11.py
@@ -2,17 +2,18 @@
# 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
+# 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.
#
-# This script is designed to run unity in a test drive manner. It will drive
+# This script is designed to run unity in a test drive manner. It will drive
# X and test the GL calls that Unity makes, so that we can easily find out if
# we are triggering graphics driver/X bugs.
-"""
-A collection of emulators for X11 - namely keyboards and mice. In the future we may
-also need other devices.
+"""A collection of emulators for X11 - namely keyboards and mice.
+
+In the future we may also need other devices.
+
"""
@@ -24,21 +25,12 @@ from Xlib.display import Display
from Xlib.ext.xtest import fake_input
import gtk.gdk
+_PRESSED_KEYS = []
+_DISPLAY = Display()
+
+
class Keyboard(object):
- '''Wrapper around xlib to make faking keyboard input possible'''
- _lame_hardcoded_keycodes = {
- 'E' : 9, # escape
- 'A' : 64,
- 'C' : 37,
- 'S' : 50,
- 'T' : 23,
- 'W' : 133,
- 'U' : 111, # up arrow
- 'D' : 116, # down arrow
- 'L' : 113, # left arrow
- 'R' : 114, # right arrow
- '1' : 67 # f1
- }
+ """Wrapper around xlib to make faking keyboard input possible."""
_special_X_keysyms = {
' ' : "space",
@@ -79,69 +71,110 @@ class Keyboard(object):
'}' : "braceright",
'~' : "asciitilde"
}
-
+
+ _keysym_translations = {
+ 'Control' : 'Control_L',
+ 'Ctrl' : 'Control_L',
+ 'Alt' : 'Alt_L',
+ 'Super' : 'Super_L',
+ 'Shift' : 'Shift_L',
+ 'Enter' : 'Return',
+ }
+
def __init__(self):
- self._display = Display()
+ self.shifted_keys = [k[1] for k in _DISPLAY._keymap_codes if k]
- def press(self, keys):
- """
- Send key press events for every key in the 'keys' string.
- """
- self.__perform_on_keys(keys, X.KeyPress)
- sleep(0.2)
+ def press(self, keys, delay=0.2):
+ """Send key press events only.
+
+ The 'keys' argument must be a string of keys you want
+ pressed. For example:
+
+ press('Alt+F2')
+
+ presses the 'Alt' and 'F2' keys.
- def release(self, keys):
- """
- Send key release events for every key in the 'keys' string.
"""
- self.__perform_on_keys(keys, X.KeyRelease)
- sleep(0.2)
-
- def press_and_release(self, keys):
+ if not isinstance(keys, basestring):
+ raise TypeError("'keys' argument must be a string.")
+ self.__perform_on_keys(self.__translate_keys(keys), X.KeyPress)
+ sleep(delay)
+
+ def release(self, keys, delay=0.2):
+ """Send key release events only.
+
+ The 'keys' argument must be a string of keys you want
+ released. For example:
+
+ release('Alt+F2')
+
+ releases the 'Alt' and 'F2' keys.
+
"""
- Send key press events for every key in the 'keys' string, then send
- key release events for every key in the 'keys' string.
+ if not isinstance(keys, basestring):
+ raise TypeError("'keys' argument must be a string.")
+ self.__perform_on_keys(self.__translate_keys(keys), X.KeyRelease)
+ sleep(delay)
+
+ def press_and_release(self, keys, delay=0.2):
+ """Press and release all items in 'keys'.
+
+ This is the same as calling 'press(keys);release(keys)'.
+
+ The 'keys' argument must be a string of keys you want
+ pressed and released.. For example:
+
+ press_and_release('Alt+F2'])
+
+ presses both the 'Alt' and 'F2' keys, and then releases both keys.
- This method is not appropriate for simulating a user typing a string
- of text, since it presses all the keys, and then releases them all.
"""
- self.press(keys)
- self.release(keys)
- def type(self, keys):
+ self.press(keys, delay)
+ self.release(keys, delay)
+
+ def type(self, string, delay=0.1):
+ """Simulate a user typing a string of text.
+
+ Only 'normal' keys can be typed with this method. Control characters
+ (such as 'Alt' will be interpreted as an 'A', and 'l', and a 't').
+
"""
- Simulate a user typing the keys specified in 'keys'.
+ if not isinstance(string, basestring):
+ raise TypeError("'keys' argument must be a string.")
+ for key in string:
+ self.press(key, delay)
+ self.release(key, delay)
+
+ @staticmethod
+ def cleanup():
+ """Generate KeyRelease events for any un-released keys.
+
+ Make sure you call this at the end of any test to release
+ any keys that were pressed and not released.
- Each key will be pressed and released before the next key is processed. If
- you need to simulate multiple keys being pressed at the same time, use the
- 'press_and_release' method above.
"""
- for key in keys:
- self.press(key)
- self.release(key)
+ for keycode in _PRESSED_KEYS:
+ print "Releasing key: %r" % (keycode)
+ fake_input(_DISPLAY, X.KeyRelease, keycode)
def __perform_on_keys(self, keys, event):
- control_key = False
keycode = 0
shift_mask = 0
- for index in range(len(keys)):
- key = keys[index]
- if control_key:
- keycode = self._lame_hardcoded_keycodes[key]
- shift_mask = 0
- control_key = False
- elif index < len(keys) and key == '^' and keys[index+1] in self._lame_hardcoded_keycodes:
- control_key = True
- continue
- else:
- keycode, shift_mask = self.__char_to_keycode(key)
+ for key in keys:
+ keycode, shift_mask = self.__char_to_keycode(key)
if shift_mask != 0:
- fake_input(self._display, event, 50)
+ fake_input(_DISPLAY, event, 50)
+
+ if event == X.KeyPress:
+ _PRESSED_KEYS.append(keycode)
+ elif event == X.KeyRelease:
+ _PRESSED_KEYS.remove(keycode)
- fake_input(self._display, event, keycode)
- self._display.sync()
+ fake_input(_DISPLAY, event, keycode)
+ _DISPLAY.sync()
def __get_keysym(self, key) :
keysym = XK.string_to_keysym(key)
@@ -151,70 +184,68 @@ class Keyboard(object):
# the subsequent display.keysym_to_keycode("numbersign") is 0.
keysym = XK.string_to_keysym(self._special_X_keysyms[key])
return keysym
-
+
def __is_shifted(self, key) :
- if key.isupper() :
- return True
- if "~!@#$%^&*()_+{}|:\"<>?".find(key) >= 0 :
- return True
- return False
+ return len(key) == 1 and ord(key) in self.shifted_keys
def __char_to_keycode(self, key) :
keysym = self.__get_keysym(key)
- keycode = self._display.keysym_to_keycode(keysym)
+ keycode = _DISPLAY.keysym_to_keycode(keysym)
if keycode == 0 :
print "Sorry, can't map", key
-
+
if (self.__is_shifted(key)) :
shift_mask = X.ShiftMask
else :
shift_mask = 0
-
return keycode, shift_mask
+ def __translate_keys(self, key_string):
+ return [self._keysym_translations.get(k,k) for k in key_string.split('+')]
+
+
class Mouse(object):
- '''Wrapper around xlib to make moving the mouse easier'''
-
- def __init__(self):
- self._display = Display()
+ """Wrapper around xlib to make moving the mouse easier."""
@property
def x(self):
+ """Mouse position X coordinate."""
return self.position()[0]
@property
def y(self):
+ """Mouse position Y coordinate."""
return self.position()[1]
-
+
def press(self, button=1):
- '''Press mouse button at current mouse location'''
- fake_input(self._display, X.ButtonPress, button)
- self._display.sync()
-
+ """Press mouse button at current mouse location."""
+ fake_input(_DISPLAY, X.ButtonPress, button)
+ _DISPLAY.sync()
+
def release(self, button=1):
- '''Releases mouse button at current mouse location'''
- fake_input(self._display, X.ButtonRelease, button)
- self._display.sync()
-
+ """Releases mouse button at current mouse location."""
+ fake_input(_DISPLAY, X.ButtonRelease, button)
+ _DISPLAY.sync()
+
def click(self, button=1):
- '''Click mouse at current location'''
+ """Click mouse at current location."""
self.press(button)
sleep(0.25)
self.release(button)
-
+
def move(self, x, y, animate=True, rate=100, time_between_events=0.001):
'''Moves mouse to location (x, y, pixels_per_event, time_between_event)'''
def perform_move(x, y, sync):
- fake_input(self._display, X.MotionNotify, sync, X.CurrentTime, X.NONE, x=x, y=y)
- self._display.sync()
+ fake_input(_DISPLAY, X.MotionNotify, sync, X.CurrentTime, X.NONE, x=x, y=y)
+ _DISPLAY.sync()
sleep(time_between_events)
if not animate:
perform_move(x, y, False)
-
+
dest_x, dest_y = x, y
curr_x, curr_y = self.position()
-
+
# calculate a path from our current position to our destination
dy = float(curr_y - dest_y)
dx = float(curr_x - dest_x)
@@ -226,24 +257,25 @@ class Mouse(object):
target_x = min(curr_x + xscale, dest_x) if dest_x > curr_x else max(curr_x + xscale, dest_x)
perform_move(target_x - curr_x, 0, True)
curr_x = target_x;
-
+
if (curr_y != dest_y):
yscale = rate if dest_y > curr_y else -rate
while (curr_y != dest_y):
target_y = min(curr_y + yscale, dest_y) if dest_y > curr_y else max(curr_y + yscale, dest_y)
perform_move(0, target_y - curr_y, True)
curr_y = target_y
-
+
def position(self):
- '''Returns the current position of the mouse pointer'''
- coord = self._display.screen().root.query_pointer()._data
+ """Returns the current position of the mouse pointer."""
+ coord = _DISPLAY.screen().root.query_pointer()._data
x, y = coord["root_x"], coord["root_y"]
return x, y
-
- def reset(self):
- self.move(16, 13, animate=False)
- self.click()
- self.move(800, 500, animate=False)
+
+ @staticmethod
+ def cleanup():
+ """Put mouse in a known safe state."""
+ sg = ScreenGeometry()
+ sg.move_mouse_to_monitor(0)
class ScreenGeometry:
diff --git a/tests/autopilot/autopilot/emulators/bamf.py b/tests/autopilot/autopilot/emulators/bamf.py
new file mode 100644
index 000000000..461e6eb22
--- /dev/null
+++ b/tests/autopilot/autopilot/emulators/bamf.py
@@ -0,0 +1,341 @@
+# Copyright 2011 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.
+
+"Various classes for interacting with BAMF."
+
+import dbus
+from dbus.mainloop.glib import DBusGMainLoop
+import gio
+import gobject
+from Xlib import display, X, protocol
+
+__all__ = ["Bamf",
+ "BamfApplication",
+ "BamfWindow",
+ ]
+
+
+_BAMF_BUS_NAME = 'org.ayatana.bamf'
+DBusGMainLoop(set_as_default=True)
+_session_bus = dbus.SessionBus()
+_X_DISPLAY = display.Display()
+
+
+def _filter_user_visible(win):
+ """Filter out non-user-visible objects.
+
+ In some cases the DBus method we need to call hasn't been registered yet,
+ in which case we do the safe thing and return False.
+
+ """
+ try:
+ return win.user_visible
+ except dbus.DBusException:
+ return False
+
+
+class Bamf:
+ """High-level class for interacting with Bamf from within a test.
+
+ Use this class to inspect the state of running applications and open
+ windows.
+
+ """
+
+ def __init__(self):
+ matcher_path = '/org/ayatana/bamf/matcher'
+ self.matcher_interface_name = 'org.ayatana.bamf.matcher'
+ self.matcher_proxy = _session_bus.get_object(_BAMF_BUS_NAME, matcher_path)
+ self.matcher_interface = dbus.Interface(self.matcher_proxy, self.matcher_interface_name)
+
+ def get_running_applications(self, user_visible_only=True):
+ """Get a list of the currently running applications.
+
+ If user_visible_only is True (the default), only applications
+ visible to the user in the switcher will be returned.
+
+ """
+ apps = [BamfApplication(p) for p in self.matcher_interface.RunningApplications()]
+ if user_visible_only:
+ return filter(_filter_user_visible, apps)
+ return apps
+
+ def get_running_applications_by_title(self, app_title):
+ """Return a list of applications that have the title `app_title`.
+
+ This method may return an empty list, if no applications
+ are found with the specified title.
+
+ """
+ return [a for a in self.get_running_applications() if a.name == app_title]
+
+ def get_open_windows(self, user_visible_only=True):
+ """Get a list of currently open windows.
+
+ If user_visible_only is True (the default), only applications
+ visible to the user in the switcher will be returned.
+
+ """
+
+ windows = [BamfWindow(w) for w in self.matcher_interface.WindowPaths()]
+ if user_visible_only:
+ return filter(_filter_user_visible, windows)
+ return windows
+
+ def get_open_windows_by_title(self, win_title):
+ """Get a list of all open windows with a specific window title.
+
+ This method may return an empty list if no currently open windows have
+ the specified title.
+
+ """
+ return [w for w in self.get_open_windows() if w.title == win_title]
+
+ def application_is_running(self, app_name):
+ """Detect if an application with a given name is currently running.
+
+ 'app_name' is the name of the application you are looking for.
+ """
+ return app_name in [a.name for a in self.get_running_applications()]
+
+ def wait_until_application_is_running(self, app_name, timeout):
+ """Wait until a given application is running.
+
+ 'app_name' is the name of the application.
+ 'timeout' is the maximum time to wait, in seconds. If set to
+ something less than 0, this method will wait forever.
+
+ This method returns true once the application is found, or false
+ if the application was not found until the timeout was reached.
+ """
+ # python workaround since you can't assign to variables in the enclosing scope:
+ # see on_timeout_reached below...
+ found_app = [True]
+
+ # maybe the app is running already?
+ if not self.application_is_running(app_name):
+ wait_forever = timeout < 0
+ gobject_loop = gobject.MainLoop()
+ # No, so define a callback to watch the ViewOpened signal:
+ def on_view_added(bamf_path, name):
+ if bamf_path.split('/')[-1].startswith('application'):
+ app = BamfApplication(bamf_path)
+ if app.name == app_name:
+ gobject_loop.quit()
+
+ # ...and one for when the user-defined timeout has been reached:
+ def on_timeout_reached():
+ gobject_loop.quit()
+ found_app[0] = False
+ return False
+
+ # need a timeout? if so, connect it:
+ if not wait_forever:
+ gobject.timeout_add(timeout * 1000, on_timeout_reached)
+ # connect signal handler:
+ _session_bus.add_signal_receiver(on_view_added, 'ViewOpened')
+ # pump the gobject main loop until either the correct signal is emitted, or the
+ # timeout happens.
+ gobject_loop.run()
+
+ return found_app[0]
+
+ def launch_application(self, desktop_file, wait=True):
+ """Launch an application by specifying a desktop file.
+
+ Returns the Gobject process object. if wait is True (the default),
+ this method will not return until an instance of this application
+ appears in the BAMF application list.
+ """
+ proc = gio.unix.DesktopAppInfo(desktop_file)
+ proc.launch()
+ if wait:
+ self.wait_until_application_is_running(proc.get_name(), -1)
+ return proc
+
+
+class BamfApplication:
+ """Represents an application, with information as returned by Bamf.
+
+ Don't instantiate this class yourself. instead, use the methods as
+ provided by the Bamf class.
+
+ """
+ def __init__(self, bamf_app_path):
+ self.bamf_app_path = bamf_app_path
+ try:
+ self._app_proxy = _session_bus.get_object(_BAMF_BUS_NAME, bamf_app_path)
+ self._view_iface = dbus.Interface(self._app_proxy, 'org.ayatana.bamf.view')
+ except dbus.DBusException, e:
+ e.message += 'bamf_app_path=%r' % (bamf_app_path)
+ raise
+
+ @property
+ def name(self):
+ """Get the application name."""
+ return self._view_iface.Name()
+
+ @property
+ def is_active(self):
+ """Is the application active (i.e.- has keyboard focus)?"""
+ return self._view_iface.IsActive()
+
+ @property
+ def is_urgent(self):
+ """Is the application currently signalling urgency?"""
+ return self._view_iface.IsUrgent()
+
+ @property
+ def user_visible(self):
+ """Is this application visible to the user?
+
+ Some applications (such as the panel) are hidden to the user but will
+ still be returned by bamf.
+
+ """
+ return self._view_iface.UserVisible()
+
+ def get_windows(self):
+ """Get a list of the application windows."""
+ return [BamfWindow(w) for w in self._view_iface.Children()]
+
+ def __repr__(self):
+ return "<BamfApplication '%s'>" % (self.name)
+
+
+class BamfWindow:
+ """Represents an application window, as returned by Bamf.
+
+ Don't instantiate this class yourself. Instead, use the appropriate methods
+ in BamfApplication.
+
+ """
+ def __init__(self, window_path):
+ self._bamf_win_path = window_path
+ self._app_proxy = _session_bus.get_object(_BAMF_BUS_NAME, window_path)
+ self._window_iface = dbus.Interface(self._app_proxy, 'org.ayatana.bamf.window')
+ self._view_iface = dbus.Interface(self._app_proxy, 'org.ayatana.bamf.view')
+
+ self._xid = int(self._window_iface.GetXid())
+ self._x_root_win = _X_DISPLAY.screen().root
+ self._x_win = _X_DISPLAY.create_resource_object('window', self._xid)
+
+
+ @property
+ def x_id(self):
+ """Get the X11 Window Id."""
+ return self._xid
+
+ @property
+ def title(self):
+ """Get the window title.
+
+ This may be different from the application name.
+
+ """
+ return self._getProperty('_NET_WM_NAME')
+
+ @property
+ def geometry(self):
+ """Get the geometry for this window.
+
+ Returns a tuple containing (x, y, width, height).
+
+ """
+
+ geometry = self._x_win.get_geometry()
+ return (geometry.x, geometry.y, geometry.width, geometry.height)
+
+ @property
+ def is_maximized(self):
+ """Is the window maximized?
+
+ Maximized in this case means both maximized
+ vertically and horizontally. If a window is only maximized in one
+ direction it is not considered maximized.
+
+ """
+ win_state = self._get_window_states()
+ return '_NET_WM_STATE_MAXIMIZED_VERT' in win_state and \
+ '_NET_WM_STATE_MAXIMIZED_HORZ' in win_state
+
+ @property
+ def application(self):
+ """Get the application that owns this window.
+
+ This method may return None if the window does not have an associated
+ application. The 'desktop' window is one such example.
+
+ """
+ # BAMF returns a list of parents since some windows don't have an
+ # associated application. For these windows we return none.
+ parents = self._view_iface.Parents()
+ if parents:
+ return BamfApplication(parents[0])
+ else:
+ return None
+
+ @property
+ def user_visible(self):
+ """Is this window visible to the user in the switcher?"""
+ return self._view_iface.UserVisible()
+
+ @property
+ def is_hidden(self):
+ """Is this window hidden?
+
+ Windows are hidden when the 'Show Desktop' mode is activated.
+
+ """
+ win_state = self._get_window_states()
+ return '_NET_WM_STATE_HIDDEN' in win_state
+
+ @property
+ def is_valid(self):
+ """Is this window object valid?
+
+ Invalid windows are caused by windows closing during the construction of
+ this object instance.
+
+ """
+ return not self._x_win is None
+
+ def close(self):
+ """Close the window."""
+
+ self._setProperty('_NET_CLOSE_WINDOW', [0, 0])
+
+ def __repr__(self):
+ return "<BamfWindow '%s'>" % (self.title if self._x_win else str(self._xid))
+
+ def _getProperty(self, _type):
+ """Get an X11 property.
+
+ _type is a string naming the property type. win is the X11 window object.
+
+ """
+ atom = self._x_win.get_full_property(_X_DISPLAY.get_atom(_type), X.AnyPropertyType)
+ if atom: return atom.value
+
+ def _setProperty(self, _type, data, mask=None):
+ if type(data) is str:
+ dataSize = 8
+ else:
+ data = (data+[0]*(5-len(data)))[:5]
+ dataSize = 32
+
+ ev = protocol.event.ClientMessage(window=self._x_win, client_type=_X_DISPLAY.get_atom(_type), data=(dataSize, data))
+
+ if not mask:
+ mask = (X.SubstructureRedirectMask|X.SubstructureNotifyMask)
+ self._x_root_win.send_event(ev, event_mask=mask)
+ _X_DISPLAY.sync()
+
+ def _get_window_states(self):
+ """Return a list of strings representing the current window state."""
+
+ return map(_X_DISPLAY.get_atom_name, self._getProperty('_NET_WM_STATE'))
diff --git a/tests/autopilot/autopilot/emulators/unity.py b/tests/autopilot/autopilot/emulators/unity.py
index dfcd89a14..4768827e1 100644
--- a/tests/autopilot/autopilot/emulators/unity.py
+++ b/tests/autopilot/autopilot/emulators/unity.py
@@ -10,9 +10,7 @@
# X and test the GL calls that Unity makes, so that we can easily find out if
# we are triggering graphics driver/X bugs.
-"""
-A collection of emulators that make it easier to interact with Unity.
-"""
+"""A collection of emulators that make it easier to interact with Unity."""
from compizconfig import Setting
from compizconfig import Plugin
@@ -23,12 +21,12 @@ from autopilot.emulators.X11 import Keyboard, Mouse, ScreenGeometry
from autopilot.globals import global_context
class Unity(object):
- '''
- High level class to abstract interactions with the unity shell.
+ """High level class to abstract interactions with the unity shell.
- This class should not be used directly. Instead, use one of the derived classes to
- interact with a different piece of the Unity system.
- '''
+ This class should not be used directly. Instead, use one of the derived
+ classes to interact with a different piece of the Unity system.
+
+ """
def __init__(self):
## TODO
@@ -48,16 +46,12 @@ class Unity(object):
self.INTROSPECTION_IFACE)
def get_state(self, piece='/Unity'):
- '''
- returns a full dump of unity's state via the introspection interface
- '''
+ """Returns a full dump of unity's state."""
return self._introspection_iface.GetState(piece)
class Launcher(Unity):
- """
- Interact with the unity Launcher.
- """
+ """Interact with the unity Launcher."""
def __init__(self):
super(Launcher, self).__init__()
@@ -70,11 +64,14 @@ class Launcher(Unity):
self.hide_timeout = 1
self.grabbed = False
+ state = self.__get_state(0)
+ self.icon_width = int(state['icon-size'])
+
def move_mouse_to_right_of_launcher(self, monitor):
(x, y, w, h) = self.launcher_geometry(monitor)
- self._mouse.move(x + w + 15, y + h / 2, False)
+ self._mouse.move(x + w + 10, y + h / 2, False)
sleep(self.show_timeout)
-
+
def move_mouse_over_launcher(self, monitor):
(x, y, w, h) = self.launcher_geometry(monitor)
self._screen.move_mouse_to_monitor(monitor);
@@ -82,57 +79,57 @@ class Launcher(Unity):
def reveal_launcher(self, monitor):
(x, y, w, h) = self.launcher_geometry(monitor)
- self._mouse.move(x - 920, y + h / 2, True, 5, .002)
+ self._mouse.move(x - 1200, y + h / 2)
sleep(self.show_timeout)
def keyboard_reveal_launcher(self):
- self._keyboard.press('^W')
+ self._keyboard.press('Super')
sleep(1)
-
+
def keyboard_unreveal_launcher(self):
- self._keyboard.release('^W')
+ self._keyboard.release('Super')
sleep(1)
def grab_switcher(self):
- self._keyboard.press_and_release('^A^1')
+ self._keyboard.press_and_release('Alt+F1')
self.grabbed = True
def switcher_enter_quicklist(self):
if self.grabbed:
- self._keyboard.press_and_release('^R')
+ self._keyboard.press_and_release('Right')
def switcher_exit_quicklist(self):
if self.grabbed:
- self._keyboard.press_and_release('^L')
+ self._keyboard.press_and_release('Left')
def start_switcher(self):
- self._keyboard.press('^W^T')
- self._keyboard.release('^T')
+ self._keyboard.press('Super+Tab')
+ self._keyboard.release('Tab')
sleep(1)
def end_switcher(self, cancel):
if cancel:
- self._keyboard.press_and_release('^E')
+ self._keyboard.press_and_release('Escape')
if self.grabbed != True:
- self._keyboard.release('^W')
+ self._keyboard.release('Super')
else:
if self.grabbed:
self._keyboard.press_and_release('\n')
else:
- self._keyboard.release('^W')
+ self._keyboard.release('Super')
self.grabbed = False
def switcher_next(self):
if self.grabbed:
- self._keyboard.press_and_release('^D')
+ self._keyboard.press_and_release('Down')
else:
- self._keyboard.press_and_release('^T')
+ self._keyboard.press_and_release('Tab')
def switcher_prev(self):
if self.grabbed:
- self._keyboard.press_and_release('^U')
+ self._keyboard.press_and_release('Up')
else:
- self._keyboard.press_and_release('^S^T')
+ self._keyboard.press_and_release('Shift+Tab')
def quicklist_open(self, monitor):
state = self.__get_state(monitor)
@@ -176,76 +173,96 @@ class Launcher(Unity):
# get the state for the 'launcher' piece
return super(Launcher, self).get_state('/Unity/LauncherController/Launcher[monitor=%s]' % (monitor))[0]
+ def get_launcher_icons(self):
+ """Get a list of launcher icons in this launcher."""
+ icons = self.get_state("//Launcher/LauncherIcon")
+ return [LauncherIcon(icon_dict) for icon_dict in icons]
-class UnityLauncherIconTooltip(Unity):
- """
- Interact with the Launcher Icon Tooltips?
- """
- RENDER_TIMEOUT_MS = 0
- SHOW_X_POS = 0
- SHOW_Y_POS = 32
- HIDE_X_POS = 64
- HIDE_Y_POS = 0
- WIDTH = 0
- HEIGHT = 0
-
- def __init__(self, TooltipText=None):
- self.mouse = Mouse()
+ def click_launcher_icon(self, icon, monitor=0, button=1):
+ """Move the mouse over the launcher icon, and click it."""
+ self.reveal_launcher(monitor)
+ self._mouse.move(icon.x, icon.y + (self.icon_width / 2))
+ self._mouse.click(button)
+ self.move_mouse_to_right_of_launcher(monitor)
- def setUp(self):
- self.mouse.move(200, 200)
+class LauncherIcon:
+ """Holds information about a launcher icon.
- def show_on_mouse_hover(self):
- self.mouse.move(self.SHOW_X_POS, self.SHOW_Y_POS)
- sleep(self.RENDER_TIMEOUT_MS)
+ Do not instantiate an instance of this class yourself. Instead, use the
+ appropriate methods in the Launcher class instead.
- def hide_on_mouse_out(self):
- self.mouse.move(self.HIDE_X_POS, self.HIDE_Y_POS)
-
- def isRendered(self):
- return False
+ """
+ def __init__(self, icon_dict):
+ self.tooltip_text = icon_dict['tooltip-text']
+ self.x = icon_dict['x']
+ self.y = icon_dict['y']
+ self.num_windows = icon_dict['related-windows']
+ self.visible = icon_dict['quirk-visible']
+ self.active = icon_dict['quirk-active']
+ self.running = icon_dict['quirk-running']
+ self.presented = icon_dict['quirk-presented']
+ self.urgent = icon_dict['quirk-urgent']
class Switcher(Unity):
- """
- Interact with the Unity switcher.
- """
+ """Interact with the Unity switcher."""
def __init__(self):
super(Switcher, self).__init__()
def initiate(self):
- self._keyboard.press('^A^T')
- self._keyboard.release('^T')
+ """Start the switcher with alt+tab."""
+ self._keyboard.press('Alt')
+ self._keyboard.press_and_release('Tab')
+ sleep(1)
def initiate_detail_mode(self):
- self._keyboard.press('^A`')
- self._keyboard.release('`')
+ """Start detail mode with alt+`"""
+ self._keyboard.press('Alt')
+ self._keyboard.press_and_release('`')
def terminate(self):
- self._keyboard.release('^A')
+ """Stop switcher."""
+ self._keyboard.release('Alt')
def next_icon(self):
- self._keyboard.press_and_release('^T')
+ """Move to the next application."""
+ self._keyboard.press_and_release('Tab')
def previous_icon(self):
- self._keyboard.press_and_release('^S^T')
+ """Move to the previous application."""
+ self._keyboard.press_and_release('Shift+Tab')
def show_details(self):
+ """Show detail mode."""
self._keyboard.press_and_release('`')
def hide_details(self):
- self._keyboard.press_and_release('^U')
+ """Hide detail mode."""
+ self._keyboard.press_and_release('Up')
def next_detail(self):
+ """Move to next detail in the switcher."""
self._keyboard.press_and_release('`')
def previous_detail(self):
- self._keyboard.press_and_release('^S`')
+ """Move to the previous detail in the switcher."""
+ self._keyboard.press_and_release('Shift+`')
def __get_icon(self, index):
- import ipdb; ipdb.set_trace()
- return self.get_state('/Unity/SwitcherController/SwitcherModel')[0]['children-of-men'][index][1][0]
+ return self.__get_model()['Children'][index][1][0]
+
+ @property
+ def current_icon(self):
+ """Get the currently-selected icon."""
+ if not self.get_is_visible:
+ return None
+ model = self.__get_model()
+ sel_idx = self.get_selection_index()
+ try:
+ return LauncherIcon(model['Children'][sel_idx][1])
+ except KeyError:
+ return None
def get_icon_name(self, index):
return self.__get_icon(index)['tooltip-text']
@@ -257,56 +274,93 @@ class Switcher(Unity):
return None
def get_model_size(self):
- return len(self.get_state('/Unity/SwitcherController/SwitcherModel')[0]['children-of-men'])
+ return len(self.__get_model()['Children'])
def get_selection_index(self):
- return int(self.get_state('/Unity/SwitcherController/SwitcherModel')[0]['selection-index'])
+ return int(self.__get_model()['selection-index'])
def get_last_selection_index(self):
- return bool(self.get_state('/Unity/SwitcherController/SwitcherModel')[0]['last-selection-index'])
+ return bool(self.__get_model()['last-selection-index'])
def get_is_visible(self):
- return bool(self.get_state('/Unity/SwitcherController')[0]['visible'])
+ return bool(self.__get_controller()['visible'])
+
+ def __get_model(self):
+ return self.get_state('/Unity/SwitcherController/SwitcherModel')[0]
+
+ def __get_controller(self):
+ return self.set_state('/unity/SwitcherController')[0]
class Dash(Unity):
- """
- An emulator class that makes it easier to interact with the unity dash.
- """
-
- def __init__(self):
- self.plugin = Plugin(global_context, "unityshell")
- self.setting = Setting(self.plugin, "show_launcher")
- super(Dash, self).__init__()
-
- def toggle_reveal(self):
- """
- Reveals the dash if it's currently hidden, hides it otherwise.
- """
- self._keyboard.press_and_release("^W")
- sleep(1)
-
- def ensure_visible(self):
- """
- Ensures the dash is visible.
- """
- if not self.get_is_visible():
- self.toggle_reveal();
-
- def ensure_hidden(self):
- """
- Ensures the dash is hidden.
- """
- if self.get_is_visible():
- self.toggle_reveal();
-
- def get_is_visible(self):
- """
- Is the dash visible?
- """
+ """
+ An emulator class that makes it easier to interact with the unity dash.
+ """
+
+ def __init__(self):
+ self.plugin = Plugin(global_context, "unityshell")
+ self.setting = Setting(self.plugin, "show_launcher")
+ super(Dash, self).__init__()
+
+ def toggle_reveal(self):
+ """
+ Reveals the dash if it's currently hidden, hides it otherwise.
+ """
+ self._keyboard.press_and_release("Super")
+ sleep(1)
+
+ def ensure_visible(self):
+ """
+ Ensures the dash is visible.
+ """
+ if not self.get_is_visible():
+ self.toggle_reveal();
+
+ def ensure_hidden(self):
+ """
+ Ensures the dash is hidden.
+ """
+ if self.get_is_visible():
+ self.toggle_reveal();
+
+ def get_is_visible(self):
+ """
+ Is the dash visible?
+ """
return bool(self.get_state("/Unity/DashController")[0]["visible"])
- def get_search_string(self):
- """
- Return the current dash search bar search string.
- """
+ def get_search_string(self):
+ """
+ Return the current dash search bar search string.
+ """
return unicode(self.get_state("//SearchBar")[0]['search_string'])
+
+ def get_current_lens(self):
+ """Returns the id of the current lens.
+
+ For example, the default lens is 'home.lens', the run-command lens is
+ 'commands.lens'.
+
+ """
+ return unicode(self.get_state("//DashController/DashView/LensBar")[0]['active-lens'])
+
+ def reveal_application_lens(self):
+ """Reveal the application lense."""
+ self._keyboard.press('Super')
+ self._keyboard.press_and_release("a")
+ self._keyboard.release('Super')
+
+ def reveal_music_lens(self):
+ """Reveal the music lense."""
+ self._keyboard.press('Super')
+ self._keyboard.press_and_release("m")
+ self._keyboard.release('Super')
+
+ def reveal_file_lens(self):
+ """Reveal the file lense."""
+ self._keyboard.press('Super')
+ self._keyboard.press_and_release("f")
+ self._keyboard.release('Super')
+
+ def reveal_command_lens(self):
+ """Reveal the 'run command' lens."""
+ self._keyboard.press_and_release('Alt+F2')
diff --git a/tests/autopilot/autopilot/tests/__init__.py b/tests/autopilot/autopilot/tests/__init__.py
index f08df6284..17904e51f 100644
--- a/tests/autopilot/autopilot/tests/__init__.py
+++ b/tests/autopilot/autopilot/tests/__init__.py
@@ -1,3 +1,14 @@
"""
Autopilot tests for Unity.
-""" \ No newline at end of file
+"""
+
+from testtools import TestCase
+from autopilot.emulators.X11 import Keyboard, Mouse
+
+class AutopilotTestCase(TestCase):
+ """Wrapper around testtools.TestCase that takes care of some cleaning."""
+
+ def tearDown(self):
+ Keyboard.cleanup()
+ Mouse.cleanup()
+ super(AutopilotTestCase, self).tearDown()
diff --git a/tests/autopilot/autopilot/tests/test_dash.py b/tests/autopilot/autopilot/tests/test_dash.py
index ceb20252a..e9f6b4024 100644
--- a/tests/autopilot/autopilot/tests/test_dash.py
+++ b/tests/autopilot/autopilot/tests/test_dash.py
@@ -1,25 +1,33 @@
-from testtools import TestCase
+# -*- 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 time import sleep
from autopilot.emulators.unity import Dash
from autopilot.emulators.X11 import Keyboard
+from autopilot.tests import AutopilotTestCase
from autopilot.glibrunner import GlibRunner
-class DashTests(TestCase):
- """
- Test the unity Dash.
- """
+class DashTests(AutopilotTestCase):
+ """Test the unity Dash."""
run_test_with = GlibRunner
-
+
def setUp(self):
super(DashTests, self).setUp()
self.dash = Dash()
+ def tearDown(self):
+ super(DashTests, self).tearDown()
+ self.dash.ensure_hidden()
+
def test_dash_reveal(self):
- """
- Ensure we can show and hide the dash.
- """
+ """Ensure we can show and hide the dash."""
self.dash.ensure_hidden()
self.assertFalse(self.dash.get_is_visible())
@@ -29,9 +37,7 @@ class DashTests(TestCase):
self.assertFalse(self.dash.get_is_visible())
def test_dash_keyboard_focus(self):
- """
- Dash must put keyboard focus on the search bar at all times.
- """
+ """Dash must put keyboard focus on the search bar at all times."""
self.dash.ensure_hidden()
self.assertFalse(self.dash.get_is_visible())
@@ -43,5 +49,30 @@ class DashTests(TestCase):
self.dash.toggle_reveal()
self.assertFalse(self.dash.get_is_visible())
+ def test_application_lens_shortcut(self):
+ """Application lense must reveal when Super+a is pressed."""
+ self.dash.ensure_hidden()
+ self.dash.reveal_application_lens()
+ self.assertEqual(self.dash.get_current_lens(), u'applications.lens')
+
+ def test_music_lens_shortcut(self):
+ """Music lense must reveal when Super+w is pressed."""
+ self.dash.ensure_hidden()
+ self.dash.reveal_music_lens()
+ self.assertEqual(self.dash.get_current_lens(), u'music.lens')
+
+ def test_file_lens_shortcut(self):
+ """File lense must reveal when Super+f is pressed."""
+ self.dash.ensure_hidden()
+ self.dash.reveal_file_lens()
+ self.assertEqual(self.dash.get_current_lens(), u'files.lens')
+
+ def test_command_lens_shortcut(self):
+ """Run Command lens must reveat on alt+F2."""
+ self.dash.ensure_hidden()
+ self.dash.reveal_command_lens()
+ self.assertEqual(self.dash.get_current_lens(), u'commands.lens')
+
+
+
-
diff --git a/tests/autopilot/autopilot/tests/test_launcher.py b/tests/autopilot/autopilot/tests/test_launcher.py
index c53831448..b3e532c1a 100644
--- a/tests/autopilot/autopilot/tests/test_launcher.py
+++ b/tests/autopilot/autopilot/tests/test_launcher.py
@@ -1,13 +1,15 @@
-from testtools import TestCase
from testtools.matchers import Equals
from testtools.matchers import LessThan
+from autopilot.tests import AutopilotTestCase
from autopilot.emulators.unity import Launcher
from autopilot.glibrunner import GlibRunner
from time import sleep
-class LauncherTests(TestCase):
+
+class LauncherTests(AutopilotTestCase):
+ """Test the launcher."""
run_test_with = GlibRunner
def setUp(self):
@@ -90,6 +92,9 @@ class LauncherTests(TestCase):
def test_reveal_on_mouse_to_edge(self):
"""Tests reveal of launchers by mouse pressure."""
+ # XXX: re-enable test when launcher reeal behavior is no longer resolution-dependant.
+ self.skipTest("Launcher reveal behavior is resolution dependant.")
+
num_launchers = self.server.num_launchers()
for x in range(num_launchers):
@@ -98,8 +103,11 @@ class LauncherTests(TestCase):
self.assertThat(self.server.is_showing(x), Equals(True))
def test_reveal_with_mouse_under_launcher(self):
- """Tests that the launcher hides properly if the
+ """Tests that the launcher hides properly if the
mouse is under the launcher when it is revealed."""
+ # XXX: re-enable test when launcher reeal behavior is no longer resolution-dependant.
+ self.skipTest("Launcher reveal behavior is resolution dependant.")
+
num_launchers = self.server.num_launchers()
for x in range(num_launchers):
@@ -107,9 +115,12 @@ class LauncherTests(TestCase):
self.server.keyboard_reveal_launcher()
self.server.keyboard_unreveal_launcher()
self.assertThat(self.server.is_showing(x), 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."""
+ # XXX: re-enable test when launcher reeal behavior is no longer resolution-dependant.
+ self.skipTest("Launcher reveal behavior is resolution dependant.")
+
num_launchers = self.server.num_launchers()
for x in range(num_launchers):
@@ -117,5 +128,5 @@ class LauncherTests(TestCase):
self.server.reveal_launcher(x)
sleep(2)
self.assertThat(self.server.is_showing(x), Equals(True))
-
+
diff --git a/tests/autopilot/autopilot/tests/test_showdesktop.py b/tests/autopilot/autopilot/tests/test_showdesktop.py
new file mode 100644
index 000000000..dedac49b9
--- /dev/null
+++ b/tests/autopilot/autopilot/tests/test_showdesktop.py
@@ -0,0 +1,138 @@
+# -*- 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 time import sleep
+from subprocess import call
+
+from autopilot.emulators.bamf import Bamf
+from autopilot.emulators.X11 import Keyboard
+from autopilot.emulators.unity import Launcher, Switcher
+from autopilot.tests import AutopilotTestCase
+from autopilot.glibrunner import GlibRunner
+
+
+class ShowDesktopTests(AutopilotTestCase):
+ """Test the 'Show Desktop' functionality."""
+ run_test_with = GlibRunner
+
+ def setUp(self):
+ super(ShowDesktopTests, self).setUp()
+ self.addCleanup(Keyboard.cleanup)
+ self.bamf = Bamf()
+
+ def launch_test_apps(self):
+ """Launch character map and calculator apps."""
+ self.bamf.launch_application("gucharmap.desktop")
+ self.addCleanup(call, ["killall", "gucharmap"])
+ self.bamf.launch_application("gcalctool.desktop")
+ self.addCleanup(call, ["killall", "gcalctool"])
+
+ def test_showdesktop_hides_apps(self):
+ """Show Desktop keyboard shortcut must hide applications."""
+ self.launch_test_apps()
+
+ # show desktop, verify all windows are hidden:
+ kb = Keyboard()
+ kb.press_and_release('Control+Alt+d')
+ self.addCleanup(kb.press_and_release, keys='Control+Alt+d')
+ sleep(1)
+ open_wins = self.bamf.get_open_windows()
+ self.assertGreaterEqual(len(open_wins), 2)
+ for win in open_wins:
+ self.assertTrue(win.is_valid)
+ self.assertTrue(win.is_hidden, "Window '%s' is not hidden after show desktop activated." % (win.title))
+
+ def test_showdesktop_unhides_apps(self):
+ """Show desktop shortcut must re-show all hidden apps."""
+ self.launch_test_apps()
+
+ # show desktop, verify all windows are hidden:
+ kb = Keyboard()
+ kb.press_and_release('Control+Alt+d')
+ sleep(1)
+ open_wins = self.bamf.get_open_windows()
+ self.assertGreaterEqual(len(open_wins), 2)
+ for win in open_wins:
+ self.assertTrue(win.is_valid)
+ self.assertTrue(win.is_hidden, "Window '%s' is not hidden after show desktop activated." % (win.title))
+
+ # un-show desktop, verify all windows are shown:
+ kb.press_and_release('Control+Alt+d')
+ sleep(1)
+ for win in self.bamf.get_open_windows():
+ self.assertTrue(win.is_valid)
+ self.assertFalse(win.is_hidden, "Window '%s' is shown after show desktop deactivated." % (win.title))
+
+ def test_unhide_single_app(self):
+ """Un-hide a single app from launcher after hiding all apps."""
+ self.launch_test_apps()
+
+ # show desktop, verify all windows are hidden:
+ kb = Keyboard()
+ kb.press_and_release('Control+Alt+d')
+ sleep(1)
+ open_wins = self.bamf.get_open_windows()
+ self.assertGreaterEqual(len(open_wins), 2)
+ for win in open_wins:
+ self.assertTrue(win.is_valid)
+ self.assertTrue(win.is_hidden, "Window '%s' is not hidden after show desktop activated." % (win.title))
+
+ # We'll un-minimise the character map - find it's launcherIcon in the launcher:
+ l = Launcher()
+
+ launcher_icons = l.get_launcher_icons()
+ found = False
+ for icon in launcher_icons:
+ if icon.tooltip_text == 'Character Map':
+ found = True
+ l.click_launcher_icon(icon)
+ self.assertTrue(found, "Could not find launcher icon in launcher.")
+
+ sleep(1)
+ for win in self.bamf.get_open_windows():
+ if win.is_valid:
+ if win.title == 'Character Map':
+ self.assertFalse(win.is_hidden, "Character map did not un-hide from launcher.")
+ else:
+ self.assertTrue(win.is_hidden, "Window '%s' should still be hidden." % (win.title))
+
+ # hide desktop - now all windows should be visible:
+ kb.press_and_release('Control+Alt+d')
+ sleep(1)
+ for win in self.bamf.get_open_windows():
+ if win.is_valid:
+ self.assertFalse(win.is_hidden, "Window '%s' is not shown after show desktop deactivated." % (win.title))
+
+ def test_showdesktop_switcher(self):
+ """Show desktop item in switcher should hide all hidden apps."""
+ self.launch_test_apps()
+
+ # show desktop, verify all windows are hidden:
+ switcher = Switcher()
+ switcher.initiate()
+ sleep(0.5)
+ found = False
+ for i in range(switcher.get_model_size()):
+ current_icon = switcher.current_icon
+ self.assertIsNotNone(current_icon)
+ if current_icon.tooltip_text == 'Show Desktop':
+ found = True
+ break
+ switcher.previous_icon()
+ sleep(0.5)
+ self.assertTrue(found, "Could not find 'Show Desktop' entry in switcher.")
+ switcher.terminate()
+ kb = Keyboard()
+ self.addCleanup(kb.press_and_release, keys='Control+Alt+d')
+
+ sleep(1)
+ open_wins = self.bamf.get_open_windows()
+ self.assertGreaterEqual(len(open_wins), 2)
+ for win in open_wins:
+ self.assertTrue(win.is_valid)
+ self.assertTrue(win.is_hidden, "Window '%s' is not hidden after show desktop activated." % (win.title))
diff --git a/tests/autopilot/autopilot/tests/test_switcher.py b/tests/autopilot/autopilot/tests/test_switcher.py
index f78f9d2c6..a4917bc99 100644
--- a/tests/autopilot/autopilot/tests/test_switcher.py
+++ b/tests/autopilot/autopilot/tests/test_switcher.py
@@ -1,8 +1,14 @@
-import gio
+# -*- 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 subprocess import call
from time import sleep
-from testtools import TestCase
from testtools.matchers import Equals
from testtools.matchers import NotEquals
@@ -11,32 +17,30 @@ from compizconfig import Plugin
from autopilot.globals import global_context
from autopilot.emulators.unity import Switcher
+from autopilot.emulators.bamf import Bamf
+from autopilot.tests import AutopilotTestCase
from autopilot.glibrunner import GlibRunner
-
-class SwitcherTests(TestCase):
+class SwitcherTests(AutopilotTestCase):
+ """Test the switcher."""
run_test_with = GlibRunner
- def launch_application(self, desktop_file):
- proc = gio.unix.DesktopAppInfo(desktop_file)
- proc.launch()
-
def set_timeout_setting(self, value):
self.setting.Value = value
global_context.Write()
-
+
def setUp(self):
self.plugin = Plugin(global_context, "unityshell")
self.setting = Setting(self.plugin, "alt_tab_timeout")
+ self.bamf = Bamf()
+
+ self.bamf.launch_application("gucharmap.desktop")
+ self.bamf.launch_application("gcalctool.desktop")
+ self.bamf.launch_application("mahjongg.desktop")
- self.launch_application("gucharmap.desktop")
- self.launch_application("gcalctool.desktop")
- self.launch_application("mahjongg.desktop")
-
- sleep(5)
super(SwitcherTests, self).setUp()
-
+
self.server = Switcher()
def tearDown(self):
@@ -52,32 +56,32 @@ class SwitcherTests(TestCase):
self.server.initiate()
sleep(.2)
-
+
start = self.server.get_selection_index()
self.server.next_icon()
sleep(.2)
-
+
end = self.server.get_selection_index()
self.server.terminate()
-
+
self.assertThat(start, NotEquals(0))
self.assertThat(end, Equals(start+1))
self.set_timeout_setting(True)
-
+
def test_switcher_move_prev(self):
self.set_timeout_setting(False)
sleep(1)
self.server.initiate()
sleep(.2)
-
+
start = self.server.get_selection_index()
self.server.previous_icon()
sleep(.2)
-
+
end = self.server.get_selection_index()
self.server.terminate()
-
+
self.assertThat(start, NotEquals(0))
self.assertThat(end, Equals(start-1))
self.set_timeout_setting(True)