diff options
| author | Andrea Azzarone <azzaronea@gmail.com> | 2012-02-06 13:24:56 +0100 |
|---|---|---|
| committer | Andrea Azzarone <azzaronea@gmail.com> | 2012-02-06 13:24:56 +0100 |
| commit | 601ef902870aee6fccd89f5a134a30ba9a7b6699 (patch) | |
| tree | 58528d1bebb2f3eb403b881f4478e2821e0b179c | |
| parent | 46016b9f6d6beee92d429ef7b049029b575bbc87 (diff) | |
| parent | feae5b47ffb88b8d49d4edd79220bf79ce6a4d6a (diff) | |
Merge thomi's branch.
(bzr r1899.2.4)
| -rw-r--r-- | manual-tests/Showdesktop.txt | 15 | ||||
| -rw-r--r-- | plugins/unityshell/src/DashView.cpp | 1 | ||||
| -rw-r--r-- | plugins/unityshell/src/Introspectable.h | 9 | ||||
| -rw-r--r-- | plugins/unityshell/src/Launcher.cpp | 3 | ||||
| -rw-r--r-- | plugins/unityshell/src/LensBar.cpp | 10 | ||||
| -rw-r--r-- | plugins/unityshell/src/SwitcherModel.cpp | 18 | ||||
| -rw-r--r-- | tests/autopilot/autopilot/emulators/X11.py | 234 | ||||
| -rw-r--r-- | tests/autopilot/autopilot/emulators/bamf.py | 341 | ||||
| -rw-r--r-- | tests/autopilot/autopilot/emulators/unity.py | 278 | ||||
| -rw-r--r-- | tests/autopilot/autopilot/tests/__init__.py | 13 | ||||
| -rw-r--r-- | tests/autopilot/autopilot/tests/test_dash.py | 57 | ||||
| -rw-r--r-- | tests/autopilot/autopilot/tests/test_launcher.py | 21 | ||||
| -rw-r--r-- | tests/autopilot/autopilot/tests/test_showdesktop.py | 138 | ||||
| -rw-r--r-- | tests/autopilot/autopilot/tests/test_switcher.py | 48 |
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) |
