[gnome-calculator] Add initial installed tests
- From: Arth Patel <arthpatel src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-calculator] Add initial installed tests
- Date: Tue, 1 Apr 2014 20:13:40 +0000 (UTC)
commit 3591d986db708aa023b3ca3fb223a8f21d9d29e5
Author: Vadim Rutkovsky <vrutkovs redhat com>
Date: Sat Mar 15 00:43:18 2014 +0100
Add initial installed tests
tests/advanced_mode.feature | 21 +++++
tests/basic_mode.feature | 84 ++++++++++++++++++++
tests/common_steps.py | 168 ++++++++++++++++++++++++++++++++++++++++
tests/environment.py | 40 ++++++++++
tests/financial_mode.feature | 21 +++++
tests/programming_mode.feature | 21 +++++
tests/steps/steps.py | 51 ++++++++++++
7 files changed, 406 insertions(+), 0 deletions(-)
---
diff --git a/tests/advanced_mode.feature b/tests/advanced_mode.feature
new file mode 100644
index 0000000..e60ba5b
--- /dev/null
+++ b/tests/advanced_mode.feature
@@ -0,0 +1,21 @@
+ advanced_mode
+Feature: Advanced mode
+
+ Background:
+ * Make sure that Calculator is running
+ * Switch to Advanced mode
+
+ Scenario Outline: Simple calculations in advanced mode
+ * Calculate "<expression>"
+ Then result is "<result>"
+
+ Examples:
+ | expression | result |
+ | 123456789 + 987654321 | 1111111110 |
+ | 987654321 + 0 | 987654321 |
+ | 987654321 - 987654322 | -1 |
+ | -98765-0 | -98765 |
+ | 3/6 | 0.5 |
+ | -8/2 | -4 |
+ | 10 / 3 * 3 | 10 |
+ | 6 / (3 * 2) | 1 |
diff --git a/tests/basic_mode.feature b/tests/basic_mode.feature
new file mode 100644
index 0000000..3f8095d
--- /dev/null
+++ b/tests/basic_mode.feature
@@ -0,0 +1,84 @@
+ basic_mode
+Feature: Basic mode
+
+ Background:
+ * Make sure that Calculator is running
+ * Switch to Basic mode
+
+ @basic_add
+ Scenario Outline: Add
+ * Calculate "<expression>"
+ Then result is "<result>"
+
+ Examples:
+ | expression | result |
+ | 123456789 + 987654321 | 1111111110 |
+ | 987654321 + 0 | 987654321 |
+ | -987 + (-14) | -1001 |
+ | -0 + 0 | 0 |
+
+ @basic_add @basic_add_overflow
+ Scenario: Add - overflow
+ * Calculate "987654321^789456 +123456789"
+ Then "Overflow: the result couldn't be calculated" error is displayed
+
+ @basic_subtract
+ Scenario Outline: Subtract
+ * Calculate "<expression>"
+ Then result is "<result>"
+
+ Examples:
+ | expression | result |
+ | 987654321 - 987654322 | -1 |
+ | -98765-0 | -98765 |
+ | 0-0 | 0 |
+ | -6-(-5) | -1 |
+
+ @basic_subtract @basic_subtract_overflow
+ Scenario: Subtract overflow
+ * Calculate "-987654321^98-987654321"
+ Then "Overflow: the result couldn't be calculated" error is displayed
+
+ @basic_multiply
+ Scenario: Multiply
+ * Calculate "10 / 3 * 3"
+ Then result is "10"
+
+ @basic_divide
+ Scenario Outline: Divide
+ * Calculate "<expression>"
+ Then result is "<result>"
+
+ Examples:
+ | expression | result |
+ | 3/6 | 0.5 |
+ | 20/8 | 2.5 |
+ | 1000000/100000 | 10 |
+ | -8/2 | -4 |
+ | -9/-7 | 1.285714286 |
+ | 3.256/0.36 | 9.044444444 |
+ | -1/1 | -1 |
+ | 0/987 | 0 |
+ | 10/3*3 | 10 |
+ | 15/99 | 0.151515152 |
+
+ @basic_divide @basic_divide_by_zero
+ Scenario: Division by zero
+ * Calculate "5/0"
+ Then "Division by zero is undefined" error is displayed
+
+ @basic_divide @basic_divide_by_zero_with_decimal_point
+ Scenario: Division by zero with decimal point
+ * Calculate "0.0/0.0"
+ Then "Division by zero is undefined" error is displayed
+
+ @basic_brackets
+ Scenario Outline: Brackets
+ * Calculate "<expression>"
+ Then result is "<result>"
+
+ Examples:
+ | expression | result |
+ | 6 / (3 * 2) | 1 |
+ | 0 * ( 1 + 2 + 3 + 4) | 0 |
+ | (((2 + 8) * (5 / 2)) - 3) | 22 |
diff --git a/tests/common_steps.py b/tests/common_steps.py
new file mode 100644
index 0000000..2b84504
--- /dev/null
+++ b/tests/common_steps.py
@@ -0,0 +1,168 @@
+# -*- 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
+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
+
+
+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()
diff --git a/tests/environment.py b/tests/environment.py
new file mode 100644
index 0000000..f48d38b
--- /dev/null
+++ b/tests/environment.py
@@ -0,0 +1,40 @@
+# -*- coding: UTF-8 -*-
+
+from time import sleep
+from dogtail.utils import isA11yEnabled, enableA11y
+if not isA11yEnabled():
+ enableA11y(True)
+
+from common_steps import App
+from dogtail.config import config
+
+
+def before_all(context):
+ """Setup gnome-calculator 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('gnome-calculator')
+
+ except Exception as e:
+ print("Error in before_all: %s" % e.message)
+
+
+def after_scenario(context, scenario):
+ """Teardown for each scenario
+ Kill gnome-calculator (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/financial_mode.feature b/tests/financial_mode.feature
new file mode 100644
index 0000000..4c0ae82
--- /dev/null
+++ b/tests/financial_mode.feature
@@ -0,0 +1,21 @@
+ financial_mode
+Feature: Financial mode
+
+ Background:
+ * Make sure that Calculator is running
+ * Switch to Financial mode
+
+ Scenario Outline: Simple calculations in financial mode
+ * Calculate "<expression>"
+ Then result is "<result>"
+
+ Examples:
+ | expression | result |
+ | 123456789 + 987654321 | 1111111110 |
+ | 987654321 + 0 | 987654321 |
+ | 987654321 - 987654322 | -1 |
+ | -98765-0 | -98765 |
+ | 3/6 | 0.5 |
+ | -8/2 | -4 |
+ | 10 / 3 * 3 | 10 |
+ | 6 / (3 * 2) | 1 |
diff --git a/tests/programming_mode.feature b/tests/programming_mode.feature
new file mode 100644
index 0000000..5a6ccf7
--- /dev/null
+++ b/tests/programming_mode.feature
@@ -0,0 +1,21 @@
+ programming_mode
+Feature: Programming mode
+
+ Background:
+ * Make sure that Calculator is running
+ * Switch to Programming mode
+
+ Scenario Outline: Simple calculations in programming mode
+ * Calculate "<expression>"
+ Then result is "<result>"
+
+ Examples:
+ | expression | result |
+ | 123456789 + 987654321 | 1111111110 |
+ | 987654321 + 0 | 987654321 |
+ | 987654321 - 987654322 | -1 |
+ | -98765-0 | -98765 |
+ | 3/6 | 0.5 |
+ | -8/2 | -4 |
+ | 10 / 3 * 3 | 10 |
+ | 6 / (3 * 2) | 1 |
diff --git a/tests/steps/steps.py b/tests/steps/steps.py
new file mode 100644
index 0000000..065205b
--- /dev/null
+++ b/tests/steps/steps.py
@@ -0,0 +1,51 @@
+# -*- coding: UTF-8 -*-
+from behave import step, then
+
+from dogtail.tree import root
+from dogtail.rawinput import typeText
+from common_steps import limit_execution_time_to, wait_until
+from time import sleep
+
+
+ step(u'Help section "{name}" is displayed')
+def help_is_displayed(context, name):
+ context.yelp = root.application('yelp')
+ frame = context.yelp.child(roleName='frame')
+ wait_until(lambda x: x.showing, frame)
+ sleep(1)
+ context.assertion.assertEquals(name, frame.name)
+
+
+ step(u'Switch to {mode:w} mode')
+def switch_to_basic_mode(context, mode):
+ context.app.child(roleName='toggle button', name='Menu').click()
+ context.app.child(roleName='radio menu item', name='%s Mode' % mode).click()
+
+
+ limit_execution_time_to(seconds=30)
+def wait_until_spinner_showing(context):
+ # Wait until spinner disappears
+ sleep(0.1)
+ spinner = context.app.child('Spinner')
+ while spinner.showing:
+ sleep(0.1)
+
+
+ step(u'Calculate "{expression}"')
+def calculate(context, expression):
+ typeText(expression)
+ context.app.child('result').click()
+ wait_until_spinner_showing(context)
+
+
+ step(u'result is "{result}"')
+def verify_result(context, result):
+ # Replace unicode negative sign to simple '-'
+ actual = context.app.child(roleName='editbar').text.replace('\xe2\x88\x92', '-')
+ assert result == actual, "Incorrect result, expected '%s' but was '%s'" % (result, actual)
+
+
+ then(u'"{message}" error is displayed')
+def error_message_displayed(context, message):
+ actual = context.app.child(roleName='editbar').parent.textentry('').text
+ assert message == actual, "Incorrect error, expected '%s' but was '%s'" % (message, actual)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]