[gnome-boxes] Add general tests
- From: Zeeshan Ali Khattak <zeeshanak src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-boxes] Add general tests
- Date: Mon, 2 Feb 2015 13:48:55 +0000 (UTC)
commit c8ddf2192f0d9948736b2d02de2dd90f5be64dd6
Author: Vladimir Benes <vbenes redhat com>
Date: Tue Jan 6 07:58:42 2015 -0500
Add general tests
Behave/Dogtail set of automated tests. Tests use 3 10 MB iso files that
are downloaded at the beginning. Installed-tests infrastructure bits are
also included so tests can be executed via gnome-desktop-testing-runner
(after running ./autogen.sh --enable-installed-tests). Test can be
executed one by one or in a batch.
More details to be found in README file.
Coverage in general section:
* start/stop
* help/credits
* download an iso
* customize memory
* start box
* search boxes
WARNING: These tests are designed to run in a prestine test environment and to
that end, remove all your VMs.
https://bugzilla.gnome.org/review?bug=736288
Makefile.am | 5 +
configure.ac | 4 +
m4/behave-installed-tests.m4 | 137 +++++++++++++++++++++++++++++
tests/README | 57 ++++++++++++
tests/common_steps.py | 194 ++++++++++++++++++++++++++++++++++++++++++
tests/environment.py | 157 ++++++++++++++++++++++++++++++++++
tests/general.feature | 130 ++++++++++++++++++++++++++++
tests/steps/creation.py | 59 +++++++++++++
tests/steps/general.py | 184 +++++++++++++++++++++++++++++++++++++++
tests/steps/utils.py | 75 ++++++++++++++++
10 files changed, 1002 insertions(+), 0 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 5607fab..1dc3ca6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -73,4 +73,9 @@ vala-clean:
win-tools.iso:
@wget -O $@
http://people.gnome.org/~teuf/gnome-boxes/boxes-unattended-win-drivers/boxes-unattended-win-drivers-LATEST.iso
+ BEHAVE_INSTALLED_TESTS_RULE@
+INSTALLED_TESTS= \
+ general.feature \
+ $(NULL)
+INSTALLED_TESTS_TYPE=session-exclusive
-include $(top_srcdir)/git.mk
diff --git a/configure.ac b/configure.ac
index cd0c487..749dd21 100644
--- a/configure.ac
+++ b/configure.ac
@@ -194,6 +194,9 @@ fi
AC_SUBST(VALA_DEBUG_FLAGS)
+dnl Installed tests
+BEHAVE_INSTALLED_TESTS
+
AC_CONFIG_FILES([
Makefile
data/Makefile
@@ -221,4 +224,5 @@ AC_MSG_NOTICE([
USB redirection support: $enable_usbredir
Smartcard support: $enable_smartcard
oVirt support: $have_govirt
+ Installed tests: $enable_installed_tests
])
diff --git a/m4/behave-installed-tests.m4 b/m4/behave-installed-tests.m4
new file mode 100644
index 0000000..3dc3fff
--- /dev/null
+++ b/m4/behave-installed-tests.m4
@@ -0,0 +1,137 @@
+# How to use the installed tests m4
+#
+# Place BEHAVE_INSTALLED_TESTS somewhere in configure.ac
+#
+# Writing your Makefile.am
+# ~~~~~~~~~~~~~~~~~~~~~~~~
+#
+# Somewhere in your Makefile.am in this test directory, you need to declare
+# the following variables:
+#
+# INSTALLED_TESTS=list of tags for tests to install
+# INSTALLED_TESTS_TYPE=session-exclusive
+#
+# First the list of tests which should be installed, followed by
+# the type of test they should be configured as. The type can
+# be 'session' or 'session-exclusive'
+#
+# More information about valid types can be found here:
+# https://wiki.gnome.org/GnomeGoals/InstalledTests
+#
+# The last variable is optional, but can be useful to configure
+# your test program to run in the installed environment as opposed
+# to the normal `make check' run.
+#
+# Then place this somewhere in your Makefile.am
+#
+# @BEHAVE_INSTALLED_TESTS_RULE@
+#
+# And the following in configure.ac
+#
+# BEHAVE_INSTALLED_TESTS
+#
+# And that's it, now your unit tests will be installed along with
+# a .test metadata file into $(pkglibexecdir) if --enable-installed-tests
+# is passed to your configure script, and will be run automatically
+# by the continuous integration servers.
+#
+# FIXME: Change the above link to point to real documentation, not
+# a gnome goal page which might disappear at some point.
+#
+# BUGS: This macro hooks into install-exec-am and install-data-am
+# which are internals of Automake. This is because Automake doesnt
+# consider the regular install-exec-local / install-exec-hook or
+# data install components unless variables have been setup for them
+# in advance.
+#
+# This doesnt seem to present a problem, but it is depending on
+# internals of Automake instead of clear documented API.
+
+# Place this in configure.ac to enable
+# the installed tests option.
+
+AC_DEFUN([BEHAVE_INSTALLED_TESTS], [
+AC_PREREQ([2.50])dnl
+AC_REQUIRE([AM_NLS])dnl
+
+ AC_PROG_INSTALL
+ AC_PROG_MKDIR_P
+ AC_PROG_LIBTOOL
+
+ AC_ARG_ENABLE(installed-tests,
+ [AC_HELP_STRING([--enable-installed-tests],
+ [enable installed unit tests [default=no]])],,
+ [enable_installed_tests="no"])
+
+ AM_CONDITIONAL([BEHAVE_INSTALLED_TESTS_ENABLED],[test "x$enable_installed_tests" = "xyes"])
+ AC_SUBST([BEHAVE_INSTALLED_TESTS_ENABLED], [$enable_installed_tests])
+
+ # Define the rule for makefiles
+ BEHAVE_INSTALLED_TESTS_RULE='
+
+ifeq ($(BEHAVE_INSTALLED_TESTS_ENABLED),yes)
+
+install-exec-am: installed-tests-exec-hook
+install-data-am: installed-tests-data-hook
+uninstall-am: uninstall-tests-hook
+
+META_DIRECTORY=${DESTDIR}${datadir}/installed-tests/${PACKAGE}
+EXEC_DIRECTORY=${DESTDIR}${pkglibexecdir}/installed-tests
+
+BEHAVE_FEATURES=$(wildcard $(srcdir)/tests/*.feature)
+BEHAVE_STEP_DEFINITION=$(wildcard $(srcdir)/tests/steps/*.py)
+BEHAVE_COMMON_FILES=$(srcdir)/tests/environment.py $(srcdir)/tests/common_steps.py
+
+FINAL_TEST_ENVIRONMENT=
+ifneq ($(INSTALLED_TESTS_ENVIRONMENT),)
+ FINAL_TEST_ENVIRONMENT="env $(INSTALLED_TESTS_ENVIRONMENT)"
+endif
+
+installed-tests-exec-hook:
+ @$(MKDIR_P) $(EXEC_DIRECTORY);
+ @for feature in $(BEHAVE_FEATURES); do
\
+ $(LIBTOOL) --mode=install $(INSTALL) --mode=777 $$feature $(EXEC_DIRECTORY);\
+ done
+ @for common_file in $(BEHAVE_COMMON_FILES); do
\
+ $(LIBTOOL) --mode=install $(INSTALL) --mode=777 $$common_file $(EXEC_DIRECTORY);\
+ done
+ @$(MKDIR_P) $(EXEC_DIRECTORY)/steps;
+ @for step_definition in $(BEHAVE_STEP_DEFINITION); do
\
+ $(LIBTOOL) --mode=install $(INSTALL) --mode=777 $$step_definition $(EXEC_DIRECTORY)/steps;\
+ done
+
+
+installed-tests-data-hook:
+ @$(MKDIR_P) $(META_DIRECTORY);
+ @for test in $(INSTALLED_TESTS); do \
+ echo "Installing $$test.test to $(META_DIRECTORY)"; \
+ echo m4_escape([[Test]]) > $(META_DIRECTORY)/$$test.test; \
+ echo "Exec=behave $(pkglibexecdir)/installed-tests -i $$test -k -f html -o $$test.html -f plain"
\
+ >> $(META_DIRECTORY)/$$test.test; \
+ echo "Type=$(INSTALLED_TESTS_TYPE)" >> $(META_DIRECTORY)/$$test.test; \
+ done
+
+uninstall-tests-hook:
+ @for feature in $(BEHAVE_FEATURES); do\
+ echo "Removing feature $(EXEC_DIRECTORY) $$feature";\
+ $(LIBTOOL) --mode=uninstall $(RM) $(EXEC_DIRECTORY)/$$feature;\
+ done
+ @for common_file in $(BEHAVE_COMMON_FILES); do\
+ echo "Removing feature $(EXEC_DIRECTORY) $$common_file";\
+ $(LIBTOOL) --mode=uninstall $(RM) $(EXEC_DIRECTORY)/$$common_file;\
+ done
+ @for step_definition in $(BEHAVE_STEP_DEFINITION); do\
+ echo "Removing feature $(EXEC_DIRECTORY)/steps $$step_definition";\
+ $(LIBTOOL) --mode=uninstall $(RM) $(EXEC_DIRECTORY)/steps/$$step_definition;\
+ done
+ @for test in $(INSTALLED_TESTS); do\
+ $(LIBTOOL) --mode=uninstall $(RM) $(META_DIRECTORY)/$$test.test;\
+ done
+
+endif
+'
+
+ # substitute @BEHAVE_INSTALLED_TESTS_RULE@ in Makefiles
+ AC_SUBST([BEHAVE_INSTALLED_TESTS_RULE])
+ m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([BEHAVE_INSTALLED_TESTS_RULE])])
+])
diff --git a/tests/README b/tests/README
new file mode 100644
index 0000000..a47ca42
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,57 @@
+Introduction
+============
+
+Tests for GNOME 3 Boxes application. Tests are written in python using Behave [1]
+and Dogtail [2] frameworks. Tests are divided into several feature files and can
+be executed accordingly.
+
+Structure
+=========
+
+* Feature Files
+ * general.feature
+ * start/stop
+ * help/credits
+ * download iso
+ * customize machine memory
+ * start box
+ * search boxes
+* Environment file
+ * everything for setting/cleaning up the environment
+* Steps
+ * Dogtail implementation of Behave steps
+* README
+
+Requirements
+============
+
+* for downstream package maintainers
+ * behave (python-behave in Fedora)
+ * dogtail
+
+Execution
+=========
+
+WARNING: These tests are designed to run in a prestine test environment and to
+ that end, remove all your VMs.
+
+* in tests directory run:
+ * behave
+ to run full test set
+
+ * behave -t $test -k
+ to run single test
+
+ * behave -i $category.feature
+ to run whole feature file
+
+Authors
+======
+
+* Vladimir Benes <vbenes redhat com>
+
+References
+==========
+
+* [1] http://pythonhosted.org/behave/
+* [2] https://fedorahosted.org/dogtail/
diff --git a/tests/common_steps.py b/tests/common_steps.py
new file mode 100644
index 0000000..156c9f9
--- /dev/null
+++ b/tests/common_steps.py
@@ -0,0 +1,194 @@
+# -*- 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
+from signal import signal, alarm, SIGALRM, SIGKILL
+from subprocess import Popen, PIPE
+from behave import step
+from gi.repository import GLib, Gio
+import fcntl, os
+
+from dogtail.rawinput import keyCombo, absoluteMotion, pressKey
+from dogtail.tree import root
+from dogtail.utils import run
+from unittest import TestCase
+
+# Create a dummy unittest class to have nice assertions
+class dummy(TestCase):
+ def runTest(self): # pylint: disable=R0201
+ assert True
+
+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)
+ 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):
+ sleep(1)
+ 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 quit(self):
+ """
+ Quit the app via 'Ctrl+Q'
+ """
+ if self.recordVideo:
+ keyCombo('<Control><Alt><Shift>R')
+
+ try:
+ #os.system("pkill -9 gnome-boxes")
+ keyCombo('<Ctrl><Q>')
+ if self.isRunning():
+ self.kill()
+ except:
+ pass
+
+ def kill(self):
+ """
+ Kill the app via 'killall'
+ """
+ try:
+ os.system("pkill -9 gnome-boxes")
+ #keyCombo('<Alt><F4>')
+ 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"
+
+ self.process = Popen(self.appCommand.split() + self.parameters.split(),
+ stdout=PIPE, stderr=PIPE, bufsize=0)
+ self.pid = self.process.pid
+
+ 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"
+
+def non_block_read(output):
+ fd = output.fileno()
+ fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+ fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+ try:
+ return output.read()
+ except:
+ return ""
+
+ 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..e61bab5
--- /dev/null
+++ b/tests/environment.py
@@ -0,0 +1,157 @@
+# -*- coding: UTF-8 -*-
+
+from time import sleep, localtime, strftime
+from dogtail.utils import isA11yEnabled, enableA11y
+if not isA11yEnabled():
+ enableA11y(True)
+from dogtail.rawinput import pressKey
+from subprocess import call
+from common_steps import App, dummy, non_block_read, ensure_app_running
+from dogtail.config import config
+import os
+from urllib2 import urlopen, URLError, HTTPError
+import sys
+
+def downloadfile(url):
+ # Open the url
+ try:
+ f = urlopen(url)
+ print "** Downloading: " + url
+
+ # Open our local file for writing
+ if not os.path.isfile("%s/Downloads/%s" % (os.path.expanduser("~"), os.path.basename(url))):
+ with open("%s/Downloads/%s" % (os.path.expanduser("~"), os.path.basename(url)), "wb") as
local_file:
+ local_file.write(f.read())
+
+ except HTTPError, e:
+ print "HTTP Error:", e.code, url
+ except URLError, e:
+ print "URL Error:", e.reason, url
+
+def before_all(context):
+ """Setup stuff
+ Being executed once before any test
+ """
+
+ try:
+ if not os.path.isfile('/tmp/boxes_configured'):
+ print "** Turning off gnome idle"
+ if call("gsettings set org.gnome.desktop.session idle-delay 0", shell=True) == 0:
+ print "PASS\n"
+ else:
+ print "FAIL: unable to turn off screensaver. This can cause failures"
+
+ # Download Core-5.3.iso and images for import if not there
+ downloadfile('http://distro.ibiblio.org/tinycorelinux/5.x/x86/archive/5.3/Core-5.3.iso')
+ downloadfile('https://dl.dropboxusercontent.com/u/93657599/vbenes/Core-5.3.vmdk')
+ downloadfile('https://dl.dropboxusercontent.com/u/93657599/vbenes/Core-5.3.qcow2')
+ call('cp ~/Downloads/Core-5.3.iso /tmp', shell=True)
+ call('touch /tmp/boxes_configured', shell=True)
+
+ # Skip dogtail actions to print to stdout
+ config.logDebugToStdOut = False
+ config.typingDelay = 0.1
+ config.childrenLimit = 500
+
+ # Include assertion object
+ context.assertion = dummy()
+
+ # Store scenario start time for session logs
+ context.log_start_time = strftime("%Y-%m-%d %H:%M:%S", localtime())
+
+ context.app_class = App('gnome-boxes')
+
+ except Exception as e:
+ print "Error in before_all: %s" % e.message
+
+def before_scenario(context, scenario):
+ pass
+
+def before_tag(context, tag):
+ if 'help' in tag:
+ os.system('pkill -9 yelp')
+
+def after_step(context, step):
+ try:
+ if step.status == 'failed' and hasattr(context, "embed"):
+ # Embed screenshot if HTML report is used
+ os.system("dbus-send --print-reply --session --type=method_call " +
+ "--dest='org.gnome.Shell.Screenshot' " +
+ "'/org/gnome/Shell/Screenshot' " +
+ "org.gnome.Shell.Screenshot.Screenshot " +
+ "boolean:true boolean:false string:/tmp/screenshot.png")
+ context.embed('image/png', open("/tmp/screenshot.png", 'r').read())
+
+ except Exception as e:
+ print "Error in after_step: %s" % str(e)
+
+def after_tag(context, tag):
+ if 'help' in tag:
+ os.system('pkill -9 yelp')
+
+def after_scenario(context, scenario):
+ """Teardown for each scenario
+ Kill gnome-boxes (in order to make this reliable we send sigkill)
+ """
+
+ try:
+
+ # Delete all boxes from GUI
+ if context.app_class.isRunning():
+ new = context.app.findChildren(lambda x: x.name == 'New')[0]
+
+ # Is new visible?
+ if not new.showing:
+ # ave to press back button if visible
+ backs = context.app.findChildren(lambda x: x.name == 'Back' and x.showing)
+ if backs:
+ backs[0].click()
+
+ # Is new finally visible?
+ new = context.app.findChildren(lambda x: x.name == 'New')[0]
+ if not new.showing:
+ # Have to press vm unnamed back button
+ panel = context.app.child('Boxes').children[0].findChildren(lambda x: x.roleName == 'panel' \
+ and
x.showing)[0]
+ buttons = panel.findChildren(lambda x: x.roleName == 'push button' and x.showing)
+ if buttons:
+ buttons[0].click()
+
+ new.grabFocus()
+ pane = context.app.child(roleName='layered pane')
+ if len(pane.children) != 0:
+ for child in pane.children:
+ child.click(button=3)
+ context.app.findChildren(lambda x: x.name == 'Delete' and x.showing)[0].click()
+ context.app.findChildren(lambda x: x.name == 'Undo' and x.showing)[0].grabFocus()
+ pressKey('Tab')
+ pressKey('Enter')
+ sleep(2)
+
+ # Attach journalctl logs
+ if hasattr(context, "embed"):
+ os.system("journalctl /usr/bin/gnome-session --no-pager -o cat --since='%s'>
/tmp/journal-session.log" \
+ %
context.log_start_time)
+ data = open("/tmp/journal-session.log", 'r').read()
+ if data:
+ context.embed('text/plain', data)
+
+ context.app_class.quit()
+
+ stdout = non_block_read(context.app_class.process.stdout)
+ stderr = non_block_read(context.app_class.process.stderr)
+
+ if stdout:
+ context.embed('text/plain', stdout)
+
+ if stderr:
+ context.embed('text/plain', stderr)
+
+ except Exception as e:
+ # Stupid behave simply crashes in case exception has occurred
+ print "Error in after_scenario: %s" % e.message
+
+ # clean all boxes
+ os.system("rm -rf ~/.config/libvirt/storage/*")
+ os.system("rm -rf ~/.cache/gnome-boxes/sources/qemu*")
+ os.system("rm -rf ~/.local/share/gnome-boxes/images/*")
diff --git a/tests/general.feature b/tests/general.feature
new file mode 100644
index 0000000..b56c60b
--- /dev/null
+++ b/tests/general.feature
@@ -0,0 +1,130 @@
+Feature: General
+
+ Background:
+ * Make sure that gnome-boxes is running
+ * Wait until overview is loaded
+
+ @open_help_via_shortcut
+ Scenario: Open help via shortcut
+ * Hit "<F1>"
+ Then Help is shown
+
+ @open_help_via_menu
+ Scenario: Open help from menu
+ * Select "Help" from supermenu
+ Then Help is shown
+
+ @open_about_via_menu
+ Scenario: Open about from menu
+ * Select "About" from supermenu
+ * Press "Credits"
+ * Press "About"
+ Then About is shown
+
+ @quit_via_panel
+ Scenario: Quit Boxes via super menu
+ * Select "Quit" from supermenu
+ Then Boxes are not running
+
+ @quit_via_shortcut
+ Scenario: Quit Boxes via shortcut
+ * Select "Quit" from supermenu
+ Then Boxes are not running
+
+ @no_boxes
+ Scenario: No boxes installed
+ Then No box is visible
+
+ @download_iso_http
+ Scenario: Download iso http
+ * Create new box from url
"http://ftp.vim.org/os/Linux/distr/tinycorelinux/5.x/x86/archive/5.2/Core-5.2.iso"
+ * Wait for "sleep 10" end
+ * Hit "Enter"
+ * Save IP for machine "Core-5"
+ * Press "back" in vm
+ Then Box "Core-5" "does" exist
+ Then Ping "Core-5"
+
+ @customize_machine_before_installation
+ Scenario: Customize machine before installation
+ * Create new box from menu "Core-5"
+ * Customize mem to 64 MB
+ * Press "Create"
+ * Wait for "sleep 10" end
+ Then "65536 KiB" is visible with command "DOM=$(virsh list |grep boxes |awk {'print $1'}); virsh dominfo
$DOM"
+
+ @rename_via_button
+ Scenario: Rename via button
+ * Initiate new box "Core-5" installation
+ * Select "Core-5" box
+ * Press "Properties"
+ * Rename "Core-5" to "Kernel-6" via "button"
+ * Press "Back"
+ * Quit Boxes
+ * Start Boxes
+ Then Box "Kernel-6" "does" exist
+
+ @rename_via_label
+ Scenario: Rename via label
+ * Initiate new box "Core-5" installation
+ * Select "Core-5" box
+ * Press "Properties"
+ * Rename "Core-5" to "Kernel-6" via "label"
+ * Press "Back"
+ * Quit Boxes
+ * Start Boxes
+ Then Box "Kernel-6" "does" exist
+
+ @start_box_from_console
+ Scenario: Start box directly from console
+ * Create new box "Core-5"
+ Then Ping "Core-5"
+ * Quit Boxes
+ * Start box name "Core-5"
+ * Type "sudo ifconfig eth0 down"
+ * Wait for "sleep 4" end
+ Then Cannot ping "Core-5"
+
+ @search_via_shortcut
+ Scenario: Search via shotcut
+ * Initiate new box "Core-5" installation
+ * Initiate new box "Core-5" installation
+ * Hit "<Ctrl><f>"
+ * Type "Core-5 2"
+ Then Box "Core-5 2" "does" exist
+ Then Box "Core-5" "does not" exist
+ * Hit "<Ctrl><a>"
+ * Type "Core"
+ Then Box "Core-5 2" "does" exist
+ Then Box "Core-5" "does" exist
+
+ @search_via_button
+ Scenario: Search via button
+ * Initiate new box "Core-5" installation
+ * Initiate new box "Core-5" installation
+ * Press "Search"
+ * Type "Core-5 2"
+ Then Box "Core-5 2" "does" exist
+ Then Box "Core-5" "does not" exist
+ * Hit "<Ctrl><a>"
+ * Type "Core"
+ Then Box "Core-5 2" "does" exist
+ Then Box "Core-5" "does" exist
+
+ @search_escape
+ Scenario: Return from search via Esc
+ * Initiate new box "Core-5" installation
+ * Initiate new box "Core-5" installation
+ * Hit "<Ctrl><f>"
+ * Type "Core-5 2"
+ Then Box "Core-5 2" "does" exist
+ Then Box "Core-5" "does not" exist
+ * Hit "Esc"
+ Then Box "Core-5 2" "does" exist
+ Then Box "Core-5" "does" exist
+
+### TBD ###
+ # local_machine_paused_after_quit
+ # import_from_system_broker
+ # detach_from_system_broker
+ # add_machine_from_system_broker_via_url
diff --git a/tests/steps/creation.py b/tests/steps/creation.py
new file mode 100644
index 0000000..c4db556
--- /dev/null
+++ b/tests/steps/creation.py
@@ -0,0 +1,59 @@
+# -*- coding: UTF-8 -*-
+
+from behave import step
+from dogtail.rawinput import typeText
+from time import sleep
+from utils import get_showing_node_name
+
+ step(u'Create new box "{name}"')
+def create_machine(context, name):
+ """
+ Create new box, wait till it finish and save IP
+ """
+ context.execute_steps(u"""
+ * Create new box from menu "%s"
+ * Press "Create"
+ * Wait for "sleep 3" end
+ * Hit "Enter"
+ * Save IP for machine "%s"
+ * Press "back" in vm
+ """ %(name, name))
+
+ step(u'Create new box from url "{url}"')
+def create_new_vm_via_url(context, url):
+ context.app.child('New').click()
+ context.app.child('Continue').click()
+ context.app.child('Enter URL').click()
+
+ typeText(url)
+ context.app.child('Continue').click()
+
+ if url.find('http') != -1:
+ half_minutes = 0
+ while half_minutes < 40:
+ half_minutes += 1
+ create = context.app.child('Create')
+ if create.sensitive and create.showing:
+ create.click()
+ break
+ else:
+ sleep(30)
+
+ step(u'Create new box from menu "{sys_name}"')
+def create_new_vm_from_menu(context, sys_name):
+ context.app.child('New').click()
+ context.app.child('Continue').click()
+ get_showing_node_name(sys_name, context.app).click()
+
+ step(u'Initiate new box "{name}" installation')
+def create_machine_no_wait(context, name):
+ """
+ Initiate new box installation, no IP saved, no wait for box readines
+ """
+ context.execute_steps(u"""
+ * Create new box from menu "%s"
+ * Press "Create"
+ * Wait for "sleep 3" end
+ * Hit "Enter"
+ * Press "back" in vm
+ """ %(name))
diff --git a/tests/steps/general.py b/tests/steps/general.py
new file mode 100644
index 0000000..8d20ceb
--- /dev/null
+++ b/tests/steps/general.py
@@ -0,0 +1,184 @@
+# -*- coding: UTF-8 -*-
+
+from behave import step
+from dogtail.tree import root
+from dogtail.rawinput import typeText, pressKey, keyCombo
+from time import sleep
+from common_steps import wait_until
+from subprocess import call, check_output, Popen
+
+ step(u'About is shown')
+def about_shown(context):
+ assert context.app.child('About Boxes') != None, "About window cannot be focused"
+
+ step(u'Box "{name}" "{state}" exist')
+def does_box_exists(context, name, state):
+ found = False
+ pane = context.app.child(roleName='layered pane')
+ for child in pane.children:
+ if child.text == name:
+ found = True
+ break
+
+ if state == 'does':
+ assert found == True, "Machine %s was not found in overview" % name
+ if state == 'does not':
+ assert found == False, "Machine %s was found in overview" % name
+
+ step(u'Boxes are not running')
+def boxes_not_running(context):
+ assert context.app_class.isRunning() != True, "Boxes window still visible"
+
+ step(u'Boxes app has "{num}" windows')
+def number_of_windows(context, num):
+ assert len(context.app.children) == int(num), "App has just %s windows not %s"
%(len(context.app.children), num)
+
+ step(u'Customize mem to 64 MB')
+def customize_vm(context):
+ context.app.child(u'Customize…').click()
+ sleep(0.5)
+ pressKey('Tab')
+ pressKey('Tab')
+ pressKey('Page_Up')
+ pressKey('Page_Up')
+
+ context.app.children[0].children[0].children[3].child('Back').click()
+ sleep(0.5)
+
+ step(u'Delete all boxes')
+def delete_all(context):
+ context.app.findChildren(lambda x: x.name == 'New')[0].grabFocus()
+ pane = context.app.child(roleName='layered pane')
+ if len(pane.children) != 0:
+ for child in pane.children:
+ child.click(button=3)
+ context.app.child('Delete').click()
+ context.app.findChildren(lambda x: x.name == 'Undo' and x.showing)[0].grabFocus()
+ pressKey('Tab')
+ pressKey('Enter')
+ sleep(4)
+
+ step(u'Go into "{vm}" box')
+def go_into_vm(context, vm):
+ pane = context.app.child(roleName='layered pane')
+ for child in pane.children:
+ if child.text == vm:
+ child.click()
+ sleep(0.5)
+ break
+
+ step(u'Help is shown')
+def help_shown(context):
+ sleep(1)
+ yelp = root.application('yelp')
+ assert yelp.child('Boxes') != None, "Yelp wasn't opened"
+
+ step(u'No box is visible')
+def no_box_sign(context):
+ assert context.app.child('No boxes found') != None
+
+ step(u'Press "{action}" in vm')
+def press_back_in_vm(context, action):
+ panel = context.app.child('Boxes').children[0].findChildren(lambda x: x.roleName == 'panel' and
x.showing)[0]
+ buttons = panel.findChildren(lambda x: x.roleName == 'push button' and x.showing)
+ if action == 'back':
+ buttons[0].click()
+ if action == 'prefs':
+ buttons[1].click()
+ sleep(0.5)
+
+ step(u'Press "{action}" in alert')
+def press_back_in_prefs(context, action):
+ button = context.app.child(roleName='alert').child(action)
+ button.click()
+ sleep(0.5)
+
+ step(u'Quit Boxes')
+def quit_boxes(context):
+ keyCombo('<Ctrl><Q>')
+ sleep(5)
+
+ step(u'Rename "{machine}" to "{name}" via "{way}"')
+def rename_vm(context, machine, name, way):
+ if way == 'button':
+ context.app.child(machine, roleName='push button').click()
+ sleep(0.5)
+ if way == 'label':
+ context.app.child('Name').parent.children[-2].child(roleName='push button').click()
+ typeText(name)
+ pressKey('Enter')
+ sleep(0.5)
+
+ step(u'Save IP for machine "{vm}"')
+def save_ip_for_vm(context, vm):
+ if not hasattr(context, 'ips'):
+ context.ips = {}
+
+ ip_cmd = "head -n 1 /var/lib/libvirt/dnsmasq/default.leases | awk {'print $3'}"
+
+ wait = 0
+ while True:
+ ip = check_output(ip_cmd, shell=True).strip()
+ cmd = "ping -q -c 1 %s > /dev/null 2>&1" % ip
+ ret = call(cmd, shell=True)
+
+ if ip in context.ips.values() or ret != 0:
+ wait += 1
+ sleep(1)
+ if wait == 80:
+ print check_output('cat /var/lib/libvirt/dnsmasq/default.leases', shell=True)
+ print context.ips.values()
+ print check_output('date', shell=True)
+ print check_output('ip a s', shell=True)
+ raise Exception("no new address cannot be found for machine %s" %vm)
+ else:
+ break
+
+ count = 1
+ for key in context.ips.keys():
+ if key.find(vm) != -1:
+ count += 1
+ if count != 1:
+ vm = vm + " %s" %count
+
+ context.ips[vm] = ip
+
+ step(u'Select "{vm}" box')
+def select_vm(context, vm):
+ pane = context.app.child(roleName='layered pane')
+ for child in pane.children:
+ if child.text == vm:
+ child.click(button='3')
+ sleep(0.2)
+ break
+
+ step(u'Select "{action}" from supermenu')
+def select_menu_action(context, action):
+ keyCombo("<Super_L><F10>")
+ if action == 'About':
+ pressKey('Down')
+ if action == 'Quit':
+ pressKey('Down')
+ pressKey('Down')
+ pressKey('Enter')
+
+ step(u'Start Boxes')
+def start_boxes(context):
+ cmd = 'gnome-boxes'
+ Popen(cmd, shell=True)
+ sleep(1)
+ context.app = root.application('gnome-boxes')
+
+ step(u'Start box name "{box}"')
+def start_boxes_via_vm(context, box):
+ cmd = 'gnome-boxes %s' %box
+ Popen(cmd, shell=True)
+ sleep(5)
+ context.app = root.application('gnome-boxes')
+
+ step(u'Wait until overview is loaded')
+def initial_page_loaded(context):
+ wait_until(lambda x: x.name != 'New', context.app)
+ context.execute_steps(u"""
+ * Delete all boxes
+ """)
diff --git a/tests/steps/utils.py b/tests/steps/utils.py
new file mode 100644
index 0000000..4c19942
--- /dev/null
+++ b/tests/steps/utils.py
@@ -0,0 +1,75 @@
+# -*- coding: UTF-8 -*-
+
+from behave import step
+from dogtail.rawinput import typeText, pressKey, keyCombo
+from time import sleep
+from subprocess import call, check_output, CalledProcessError, STDOUT
+
+def get_showing_node_name(name, parent, timeout=30, step=0.25):
+ sleep = 0
+ while len(parent.findChildren(lambda x: x.name == name and x.showing and x.sensitive)) == 0:
+ sleep(step)
+ sleep += step
+ if sleep == timeout:
+ raise Exception("Timeout: Node %s wasn't found showing" %name)
+
+ return parent.findChildren(lambda x: x.name == name and x.showing and x.sensitive)[0]
+
+def get_showing_node_rolename(rolename, parent, timeout=30, step=0.25):
+ sleep = 0
+ while len(parent.findChildren(lambda x: x.roleName == rolename and x.showing and x.sensitive)) == 0:
+ sleep(step)
+ sleep += 1
+ if sleep == timeout:
+ raise Exception("Timeout: Node %s wasn't found showing" %rolename)
+
+ return parent.findChildren(lambda x: x.roleName == rolename and x.showing and x.sensitive)[0]
+
+ step(u'Cannot ping "{vm}"')
+def cannot_ping_vm(context, vm):
+ cmd = "ping -qn -c 1 %s" %context.ips[vm]
+ assert call(cmd, shell=True) != 0, "Machine %s is pingable!" %vm
+
+ step(u'Hit "{keycombo}"')
+def hit_keycombo(context, keycombo):
+ sleep(0.2)
+ if keycombo == "Enter":
+ pressKey("Enter")
+ else:
+ keyCombo('%s'%keycombo)
+
+ sleep(0.2)
+
+ step(u'"{pattern}" is visible with command "{command}"')
+def check_pattern_visible(context, pattern, command):
+ sleep(0.2) # time for all to get set
+ try:
+ out = check_output(command, stderr=STDOUT, shell=True)
+ except CalledProcessError as e:
+ out = e.output
+ assert out.find(pattern) != -1, 'pattern %s is not visible with %s' % (pattern, command)
+
+ step(u'"{pattern}" is not visible with command "{command}"')
+def check_pattern_not_visible(context, pattern, command):
+ sleep(0.2) # time for all to get set
+ out = check_output(command, shell=True)
+ assert out.find(pattern) == -1, 'pattern %s is visible with %s' % (pattern, command)
+
+ step(u'Ping "{vm}"')
+def ping_vm(context, vm):
+ cmd = "ping -qn -c 2 %s > /dev/null 2>&1" %context.ips[vm]
+ assert call(cmd, shell=True) == 0, "Machine %s is not pingable" %vm
+
+ step(u'Press "{button}"')
+def press_button(context, button):
+ get_showing_node_name(button, context.app).click()
+ sleep(0.5)
+
+ step(u'Type "{text}"')
+def type_text(context, text):
+ typeText(text)
+ pressKey('Enter')
+
+ step(u'Wait for "{cmd}" end')
+def wait_for_cmd(context, cmd):
+ call(cmd, shell=True)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]