[eog] Add initial installed tests



commit 8dffb2f1c34c25ccfd48c2a45acd035e386e7ace
Author: Vadim Rutkovsky <vrutkovs redhat com>
Date:   Thu Apr 10 18:13:19 2014 +0200

    Add initial installed tests

 tests/actions.feature |   67 +++++++++++++
 tests/common_steps.py |  245 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/environment.py  |   47 ++++++++++
 tests/gnome-logo.png  |  Bin 0 -> 11104 bytes
 tests/steps/steps.py  |  131 ++++++++++++++++++++++++++
 5 files changed, 490 insertions(+), 0 deletions(-)
---
diff --git a/tests/actions.feature b/tests/actions.feature
new file mode 100644
index 0000000..33847cc
--- /dev/null
+++ b/tests/actions.feature
@@ -0,0 +1,67 @@
+Feature: Smoke tests
+
+  Background:
+    * Make sure that eog is running
+
+  @about
+  Scenario: About dialog
+    * Open About dialog
+    Then Website link to wiki is displayed
+     And GPL 2.0 link is displayed
+
+  @undo @undo_via_toolbar
+  Scenario: Undo via toolbar
+    * Open "/tmp/gnome-logo.png" via menu
+    Then image size is 199x76
+    * Rotate the image clockwise
+    Then image size is 76x199
+    * Select "Edit -> Undo" menu
+    Then image size is 199x76
+
+  @undo @undo_via_shortcut
+  Scenario: Undo via shortcut
+    * Open "/tmp/gnome-logo.png" via menu
+    Then image size is 199x76
+    * Rotate the image clockwise
+    Then image size is 76x199
+    * Press "<Ctrl>z"
+    Then image size is 199x76
+
+  @sidepane @sidepane_via_menu
+  Scenario: Sidepanel via menu
+    * Open "/tmp/gnome-logo.png" via menu
+    Then sidepanel is displayed
+    * Select "View -> Side Pane" menu
+    Then sidepanel is hidden
+
+  @sidepane @sidepane_via_shortcut
+  Scenario: Slideshow via shortcut
+    * Open "/tmp/gnome-logo.png" via menu
+    * Press "<Ctrl><F9>"
+    Then sidepanel is hidden
+    * Select "View -> Side Pane" menu
+    Then sidepanel is displayed
+
+  @fullscreen @fullscreen_via_menu
+  Scenario: Fullscreen via menu
+    * Open "/tmp/gnome-logo.png" via menu
+    * Select "View -> Fullscreen" menu
+    Then application is displayed fullscreen
+    * Press "<Esc>"
+    Then application is not fullscreen anymore
+
+  @fullscreen @fullscreen_via_shortcut
+  Scenario: Fullscreen via shortcut
+    * Open "/tmp/gnome-logo.png" via menu
+    * Press "<F11>"
+    Then application is displayed fullscreen
+    * Press "<Esc>"
+    Then application is not fullscreen anymore
+
+  @wallpaper
+  Scenario: Set as wallpaper
+    * Open "/tmp/gnome-logo.png" via menu
+    * Select "Set as Wallpaper" from context menu
+    * Click "Hide" in wallpaper popup
+    Then wallpaper is set to "gnome-logo.png"
+
diff --git a/tests/common_steps.py b/tests/common_steps.py
new file mode 100644
index 0000000..adda7b5
--- /dev/null
+++ b/tests/common_steps.py
@@ -0,0 +1,245 @@
+# -*- coding: UTF-8 -*-
+from dogtail.utils import isA11yEnabled, enableA11y
+if isA11yEnabled() is False:
+    enableA11y(True)
+
+from time import time, sleep
+from functools import wraps
+from os import strerror, errno, kill, system, path, getcwd
+from signal import signal, alarm, SIGALRM, SIGKILL
+from subprocess import Popen
+from behave import step
+from gi.repository import GLib
+
+from dogtail.rawinput import keyCombo, absoluteMotion, pressKey
+from dogtail.tree import root
+from dogtail.utils import run
+from dogtail.predicate import GenericPredicate
+import pyatspi
+
+
+def cleanup():
+    for schema in ['org.gnome.eog.fullscreen', 'org.gnome.eog.plugins', 'org.gnome.eog.ui', 
'org.gnome.eog.view']:
+        system("gsettings reset-recursively %s" % schema)
+
+    # Remove all the remains of other files
+    system("rm /tmp/gnome-logo.bmp -rf")
+
+    # Make sure we have a test file present
+    testfile_path = path.join(path.dirname(path.realpath(__file__)), "gnome-logo.png")
+    system("cp %s /tmp" % testfile_path)
+
+
+def wait_until(my_lambda, element, timeout=30, period=0.25):
+    """
+    This function keeps running lambda with specified params until the result is True
+    or timeout is reached
+    Sample usages:
+     * wait_until(lambda x: x.name != 'Loading...', context.app.instance)
+       Pause until window title is not 'Loading...'.
+       Return False if window title is still 'Loading...'
+       Throw an exception if window doesn't exist after default timeout
+
+     * wait_until(lambda element, expected: x.text == expected, element, ('Expected text'))
+       Wait until element text becomes the expected (passed to the lambda)
+
+    """
+    exception_thrown = None
+    mustend = int(time()) + timeout
+    while int(time()) < mustend:
+        try:
+            if my_lambda(element):
+                return True
+        except Exception as e:
+            # If lambda has thrown the exception we'll re-raise it later
+            # and forget about if lambda passes
+            exception_thrown = e
+        sleep(period)
+    if exception_thrown:
+        raise exception_thrown
+    else:
+        return False
+
+
+class TimeoutError(Exception):
+    """
+    Timeout exception class for limit_execution_time_to function
+    """
+    pass
+
+
+def limit_execution_time_to(
+        seconds=10, error_message=strerror(errno.ETIME)):
+    """
+    Decorator to limit function execution to specified limit
+    """
+    def decorator(func):
+        def _handle_timeout(signum, frame):
+            raise TimeoutError(error_message)
+
+        def wrapper(*args, **kwargs):
+            signal(SIGALRM, _handle_timeout)
+            alarm(seconds)
+            try:
+                result = func(*args, **kwargs)
+            finally:
+                alarm(0)
+            return result
+
+        return wraps(func)(wrapper)
+
+    return decorator
+
+
+class App(object):
+    """
+    This class does all basic events with the app
+    """
+    def __init__(
+        self, appName, shortcut='<Control><Q>', a11yAppName=None,
+            forceKill=True, parameters='', recordVideo=False):
+        """
+        Initialize object App
+        appName     command to run the app
+        shortcut    default quit shortcut
+        a11yAppName app's a11y name is different than binary
+        forceKill   is the app supposed to be kill before/after test?
+        parameters  has the app any params needed to start? (only for startViaCommand)
+        recordVideo start gnome-shell recording while running the app
+        """
+        self.appCommand = appName
+        self.shortcut = shortcut
+        self.forceKill = forceKill
+        self.parameters = parameters
+        self.internCommand = self.appCommand.lower()
+        self.a11yAppName = a11yAppName
+        self.recordVideo = recordVideo
+        self.pid = None
+
+        # a way of overcoming overview autospawn when mouse in 1,1 from start
+        pressKey('Esc')
+        absoluteMotion(100, 100, 2)
+
+        # attempt to make a recording of the test
+        if self.recordVideo:
+            keyCombo('<Control><Alt><Shift>R')
+
+    def isRunning(self):
+        """
+        Is the app running?
+        """
+        if self.a11yAppName is None:
+            self.a11yAppName = self.internCommand
+
+        # Trap weird bus errors
+        for attempt in xrange(0, 10):
+            try:
+                return self.a11yAppName in [x.name for x in root.applications()]
+            except GLib.GError:
+                continue
+        raise Exception("10 at-spi errors, seems that bus is blocked")
+
+    def kill(self):
+        """
+        Kill the app via 'killall'
+        """
+        if self.recordVideo:
+            keyCombo('<Control><Alt><Shift>R')
+
+        try:
+            kill(self.pid, SIGKILL)
+        except:
+            # Fall back to killall
+            Popen("killall " + self.appCommand, shell=True).wait()
+
+    def startViaCommand(self):
+        """
+        Start the app via command
+        """
+        if self.forceKill and self.isRunning():
+            self.kill()
+            assert not self.isRunning(), "Application cannot be stopped"
+
+        command = "%s %s" % (self.appCommand, self.parameters)
+        self.pid = run(command, timeout=1)
+
+        assert self.isRunning(), "Application failed to start"
+        return root.application(self.a11yAppName)
+
+    def closeViaShortcut(self):
+        """
+        Close the app via shortcut
+        """
+        if not self.isRunning():
+            raise Exception("App is not running")
+
+        keyCombo(self.shortcut)
+        assert not self.isRunning(), "Application cannot be stopped"
+
+
+ step(u'Make sure that {app} is running')
+def ensure_app_running(context, app):
+    context.app = context.app_class.startViaCommand()
+
+
+ step(u'Press "{sequence}"')
+def press_button_sequence(context, sequence):
+    keyCombo(sequence)
+    sleep(0.5)
+
+
+ step(u'Folder select dialog with name "{name}" is displayed')
+def has_folder_select_dialog_with_name(context, name):
+    has_files_select_dialog_with_name(context, name)
+
+
+ step(u'Folder select dialog is displayed')
+def has_folder_select_dialog(context):
+    context.execute_steps(
+        u'Then folder select dialog with name "Select Folder" is displayed')
+
+
+ step(u'In folder select dialog choose "{name}"')
+def select_folder_in_dialog(context, name):
+    select_file_in_dialog(context, name)
+
+
+ step(u'file select dialog with name "{name}" is displayed')
+def has_files_select_dialog_with_name(context, name):
+    context.app.dialog = context.app.child(name=name,
+                                           roleName='file chooser')
+
+
+ step(u'File select dialog is displayed')
+def has_files_select_dialog(context):
+    context.execute_steps(
+        u'Then file select dialog with name "Select Files" is displayed')
+
+ step(u'In file select dialog select "{name}"')
+def select_file_in_dialog(context, name):
+    # Find an appropriate button to click
+    # It will be either 'Home' or 'File System'
+
+    home_folder = context.app.dialog.findChild(GenericPredicate(name='Home'),
+                                               retry=False,
+                                               requireResult=False)
+    if home_folder:
+        home_folder.click()
+    else:
+        context.app.dialog.childNamed('File System').click()
+    location_button = context.app.dialog.child('Type a file name')
+    if not pyatspi.STATE_ARMED in location_button.getState().getStates():
+        location_button.click()
+
+    context.app.dialog.childLabelled('Location:').set_text_contents(name)
+    sleep(0.1)
+    context.app.dialog.childLabelled('Location:').grab_focus()
+    keyCombo('<Enter>')
+    assert wait_until(lambda x: x.dead, context.app.dialog), "Dialog was not closed"
+
+
+ step(u'In file save dialog save file to "{path}" clicking "{button}"')
+def file_save_to_path(context, path, button):
+    context.app.dialog.childLabelled('Name:').set_text_contents(path)
+    context.app.dialog.childNamed(button).click()
+    assert wait_until(lambda x: x.dead, context.app.dialog), "Dialog was not closed"
diff --git a/tests/environment.py b/tests/environment.py
new file mode 100644
index 0000000..08dc06d
--- /dev/null
+++ b/tests/environment.py
@@ -0,0 +1,47 @@
+# -*- coding: UTF-8 -*-
+
+from time import sleep
+from dogtail.utils import isA11yEnabled, enableA11y
+if not isA11yEnabled():
+    enableA11y(True)
+
+from common_steps import App, cleanup
+from dogtail.config import config
+
+
+def before_all(context):
+    """Setup eog stuff
+    Being executed before all features
+    """
+
+    try:
+        # Skip dogtail actions to print to stdout
+        config.logDebugToStdOut = False
+        config.typingDelay = 0.2
+
+        context.app_class = App('eog')
+
+    except Exception as e:
+        print("Error in before_all: %s" % e.message)
+
+def before_scenario(context, scenario):
+    """ Cleanup previous settings and make sure we have test files in /tmp """
+    try:
+        cleanup()
+    except Exception as e:
+        print("Error in before_scenario: %s" % e.message)
+
+
+def after_scenario(context, scenario):
+    """Teardown for each scenario
+    Kill eog (in order to make this reliable we send sigkill)
+    """
+    try:
+        # Stop gnome-calculator
+        context.app_class.kill()
+
+        # Make some pause after scenario
+        sleep(1)
+    except Exception as e:
+        # Stupid behave simply crashes in case exception has occurred
+        print("Error in after_scenario: %s" % e.message)
diff --git a/tests/gnome-logo.png b/tests/gnome-logo.png
new file mode 100644
index 0000000..e5ea779
Binary files /dev/null and b/tests/gnome-logo.png differ
diff --git a/tests/steps/steps.py b/tests/steps/steps.py
new file mode 100644
index 0000000..9010c5d
--- /dev/null
+++ b/tests/steps/steps.py
@@ -0,0 +1,131 @@
+# -*- coding: UTF-8 -*-
+from behave import step, then
+
+from dogtail.tree import root
+from dogtail.rawinput import typeText
+from common_steps import *
+from time import sleep
+from dogtail.rawinput import keyCombo
+from subprocess import Popen, PIPE
+
+
+ step(u'Open About dialog')
+def open_about_dialog(context):
+    context.app.menu('Help').click()
+    context.app.menu('Help').menuItem('About').click()
+    context.about_dialog = context.app.dialog('About Image Viewer')
+
+ then(u'Website link to wiki is displayed')
+def website_link_to_wiki_is_displayed(context):
+    assert context.about_dialog.child('Website').showing
+
+ then(u'GPL 2.0 link is displayed')
+def gpl_license_link_is_displayed(context):
+      assert context.about_dialog.child("Image Viewer").showing, "App name is not displayed"
+      assert context.about_dialog.child("The GNOME image viewer.").showing, "App description is not 
displayed"
+      assert context.about_dialog.child("Website").showing, "Website link is not displayed"
+      assert context.about_dialog.child(roleName='radio button', name="About").checked, "About tab is not 
selected"
+      assert not context.about_dialog.child(roleName='radio button', name="Credits").checked, "Credits tab 
is selected"
+
+ step(u'Open "{filename}" via menu')
+def open_file_via_menu(context, filename):
+    keyCombo("<Ctrl>O")
+    context.execute_steps(u"""
+        * file select dialog with name "Open Image" is displayed
+        * In file select dialog select "%s"
+    """ % filename)
+    sleep(0.5)
+
+ then(u'image size is {width:d}x{height:d}')
+def image_size_is(context, width, height):
+    for attempt in xrange(0, 10):
+        width_text = context.app.child(roleName='page tab list').child('Width:').parent.children[-1].text
+        if width_text == '':
+            sleep(0.5)
+            continue
+        else:
+            break
+    height_text = context.app.child(roleName='page tab list').child('Height:').parent.children[-1].text
+    try:
+        actual_width = int(width_text.split(' ')[0])
+        actual_height = int(height_text.split(' ')[0])
+    except Exception:
+        raise Exception("Incorrect width/height is been displayed")
+    assert actual_width == width
+    assert actual_height == height
+
+ step(u'Rotate the image clockwise')
+def rotate_image_clockwise(context):
+    context.app.child(roleName='tool bar').child('Right').click()
+
+ step(u'Select "{item}" from context menu')
+def select_item_from_context_menu(context, item):
+    context.app.child(roleName='drawing area').click(button=3)
+    sleep(0.1)
+    context.app.child(roleName='window').menuItem(item).click()
+
+ then(u'sidepanel is {state:w}')
+def sidepanel_displayed(context, state):
+    sleep(0.5)
+    assert state in ['displayed', 'hidden'], "Incorrect state: %s" % state
+    actual = context.app.child(roleName='page tab list').showing
+    assert actual == (state == 'displayed')
+
+def app_is_not_fullscreen(context):
+    import ipdb; ipdb.set_trace()
+
+ then(u'application is {negative:w} fullscreen anymore')
+ then(u'application is displayed fullscreen')
+def app_displayed_fullscreen(context, negative=None):
+    sleep(0.5)
+    actual = not context.app.child(roleName='menu bar').showing
+    assert actual == (negative is None)
+
+ step(u'Wait a second')
+def wait_a_second(context):
+    sleep(1)
+
+ step(u'Click "Hide" in wallpaper popup')
+def hide_wallapper_popup(context):
+    context.app.button('Hide').click()
+
+ then(u'wallpaper is set to "{filename}"')
+def wallpaper_is_set_to(context, filename):
+    wallpaper_path = Popen(["gsettings", "get", "org.gnome.desktop.background", "picture-uri"], 
stdout=PIPE).stdout.read()
+    actual_filename = wallpaper_path.split('/')[-1].split("'")[0]
+    assert filename == actual_filename
+
+ then(u'"{filename}" file exists')
+def file_exists(context, filename):
+    assert os.path.isfile(os.path.expanduser(filename))
+
+ then(u'image type is "{mimetype}"')
+def image_type_is(context, mimetype):
+    imagetype = context.app.child(roleName='page tab list').child('Type:').parent.children[-1].text
+    assert imagetype == mimetype
+
+ step(u'Select "{menu}" menu')
+def select_menuitem(context, menu):
+    menu_item = menu.split(' -> ')
+    # First level menu
+    current = context.app.menu(menu_item[0])
+    current.click()
+    if len(menu_item) == 1:
+        return
+    # Intermediate menus - point them but don't click
+    for item in menu_item[1:-1]:
+        current = context.app.menu(item)
+        current.point()
+    # Last level menu item
+    current.menuItem(menu_item[-1]).click()
+
+ step(u'Open "Image -> Save As" menu')
+def open_save_as_menu(context):
+    context.app.menu("Image").click()
+    context.app.menu("Image").findChildren(lambda x: 'Save As' in x.name)[0].click()
+
+ step(u'Select "{name}" window')
+def select_name_window(context, name):
+    context.app = context.app.child(roleName='frame', name=name)
+    context.app.grab_focus()
+


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]