tests: Add test_ui target for dogtail UI tests, add an example

This commit is contained in:
Cole Robinson
2015-09-06 18:37:49 -04:00
parent 6e9195cf2f
commit 91e2d502bb
4 changed files with 198 additions and 22 deletions

View File

@@ -349,6 +349,8 @@ class TestBaseCommand(Command):
('regenerate-output', None, 'Regenerate test output'),
("only=", None,
"Run only testcases whose name contains the passed string"),
("testfile=", None, "Specific test file to run (e.g "
"validation, storage, ...)"),
]
def initialize_options(self):
@@ -358,11 +360,31 @@ class TestBaseCommand(Command):
self.only = None
self._testfiles = []
self._dir = os.getcwd()
self.testfile = None
def finalize_options(self):
if self.debug and "DEBUG_TESTS" not in os.environ:
os.environ["DEBUG_TESTS"] = "1"
def _find_tests_in_dir(self, dirname, excludes):
testfiles = []
for t in sorted(glob.glob(os.path.join(self._dir, dirname, '*.py'))):
base = os.path.basename(t)
if base in excludes + ["__init__.py"]:
continue
if self.testfile:
check = os.path.basename(self.testfile)
if base != check and base != (check + ".py"):
continue
testfiles.append('.'.join(
dirname.split("/") + [os.path.splitext(base)[0]]))
if not testfiles:
raise RuntimeError("--testfile didn't catch anything")
return testfiles
def run(self):
try:
import coverage
@@ -429,14 +451,11 @@ class TestBaseCommand(Command):
class TestCommand(TestBaseCommand):
description = "Runs a quick unit test suite"
user_options = TestBaseCommand.user_options + [
("testfile=", None, "Specific test file to run (e.g "
"validation, storage, ...)"),
("skipcli", None, "Skip CLI tests"),
]
def initialize_options(self):
TestBaseCommand.initialize_options(self)
self.testfile = None
self.skipcli = None
def finalize_options(self):
@@ -446,22 +465,10 @@ class TestCommand(TestBaseCommand):
'''
Finds all the tests modules in tests/, and runs them.
'''
testfiles = []
for t in sorted(glob.glob(os.path.join(self._dir, 'tests', '*.py'))):
if (t.endswith("__init__.py") or
t.endswith("test_urls.py") or
t.endswith("test_inject.py")):
continue
base = os.path.basename(t)
if self.testfile:
check = os.path.basename(self.testfile)
if base != check and base != (check + ".py"):
continue
if self.skipcli and base.count("clitest"):
continue
testfiles.append('.'.join(['tests', os.path.splitext(base)[0]]))
excludes = ["test_urls.py", "test_inject.py"]
if self.skipcli:
excludes += ["clitest.py"]
testfiles = self._find_tests_in_dir("tests", excludes)
# Put clitest at the end, since it takes the longest
for f in testfiles[:]:
@@ -476,13 +483,18 @@ class TestCommand(TestBaseCommand):
if not self.testfile and not self.skipcli:
testfiles.append(f)
if not testfiles:
raise RuntimeError("--testfile didn't catch anything")
self._testfiles = testfiles
TestBaseCommand.run(self)
class TestUI(TestBaseCommand):
description = "Run UI dogtails tests"
def run(self):
self._testfiles = self._find_tests_in_dir("tests/uitests", [])
TestBaseCommand.run(self)
class TestURLFetch(TestBaseCommand):
description = "Test fetching kernels and isos from various distro trees"
@@ -626,6 +638,7 @@ setup(
'pylint': CheckPylint,
'rpm': my_rpm,
'test': TestCommand,
'test_ui': TestUI,
'test_urls' : TestURLFetch,
'test_initrd_inject' : TestInitrdInject,
}

26
tests/uitests/__init__.py Normal file
View File

@@ -0,0 +1,26 @@
import os
import sys
import warnings
# Dogtail is noisy with GTK and GI deprecation warnings
warnings.simplefilter("ignore")
import dogtail.config
# Perform 5 search attempts if a widget lookup fails (default 20)
dogtail.config.config.searchCutoffCount = 5
# Use .4 second delay between each action (default 1)
dogtail.config.config.actionDelay = .4
# Turn off needlessly noisy debugging
DOGTAIL_DEBUG = False
dogtail.config.config.logDebugToStdOut = DOGTAIL_DEBUG
dogtail.config.config.logDebugToFile = DOGTAIL_DEBUG
# Dogtail screws with the default excepthook, disabling output if we turned
# off logging, so fix it
sys.excepthook = sys.__excepthook__
# Needed so labels are matched in english
os.environ['LANG'] = 'en_US.UTF-8'

63
tests/uitests/newvm.py Normal file
View File

@@ -0,0 +1,63 @@
import time
import unittest
import tests
import tests.uitests
class NewVM(unittest.TestCase):
"""
UI tests for virt-manager's NewVM wizard
"""
def setUp(self):
self.app = tests.uitests.utils.DogtailApp(tests.utils.uri_test)
def tearDown(self):
self.app.proc.kill()
###################
# Private helpers #
###################
def _open_create_wizard(self):
self.app.find_pattern(self.app.root, "New", "push button").click()
return self.app.find_pattern(self.app.root, "New VM", "frame")
##############
# Test cases #
##############
def testNewVMDefault(self):
"""
Click through the New VM wizard with default values + PXE, then
delete the VM
"""
# Create default PXE VM
newvm = self._open_create_wizard()
self.app.find_fuzzy(newvm, "PXE", "radio").click()
self.app.find_fuzzy(newvm, "Forward", "button").click()
self.app.find_fuzzy(newvm, "Forward", "button").click()
self.app.find_fuzzy(newvm, "Forward", "button").click()
self.app.find_fuzzy(newvm, "Forward", "button").click()
self.app.find_fuzzy(newvm, "Finish", "button").click()
# Delete it from the VM window
vmwindow = self.app.find_fuzzy(self.app.root, "generic on", "frame")
self.app.find_pattern(vmwindow, "Virtual Machine", "menu").click()
self.app.find_pattern(vmwindow, "Delete", "menu item").click()
delete = self.app.find_fuzzy(self.app.root, "Delete", "frame")
self.app.find_fuzzy(delete, "Delete", "button").click()
alert = self.app.find_pattern(self.app.root, "Warning", "alert")
self.app.find_fuzzy(alert, "Yes", "push button").click()
time.sleep(1)
# Verify delete dialog and VM dialog are now gone
self.assertFalse(delete.showing)
self.assertFalse(vmwindow.showing)
# Close the app from the main window
self.app.find_pattern(self.app.root, "File", "menu").click()
self.app.find_pattern(self.app.root, "Quit", "menu item").click()
time.sleep(.5)

74
tests/uitests/utils.py Normal file
View File

@@ -0,0 +1,74 @@
import os
import re
import time
import subprocess
import dogtail.tree
class DogtailApp(object):
"""
Wrapper class to simplify dogtail app handling
"""
def __init__(self, uri):
self.proc = subprocess.Popen(["python",
os.path.join(os.getcwd(), "virt-manager"),
"--test-first-run", "--no-fork", "--connect", uri])
time.sleep(1)
self.root = dogtail.tree.root.application("virt-manager")
@staticmethod
def find_pattern(root, name, roleName=None):
"""
Search root for any widget that contains the passed name/role regex
strings.
"""
name_pattern = re.compile(name)
role_pattern = re.compile(roleName or ".*")
def _walk(node):
try:
if not name_pattern.match(node.name):
return
if not role_pattern.match(node.roleName):
return
return node
except Exception, e:
print "got walk exception: %s" % e
ret = root.findChildren(_walk, isLambda=True)
if not ret:
raise RuntimeError("Didn't find widget with name='%s' "
"roleName='%s'" % (name, roleName))
if len(ret) > 1:
raise RuntimeError("Found more than 1 widget with name='%s' "
"rolename='%s':\n%s" % (name, roleName,
[str(w) for w in ret]))
return ret[0]
@staticmethod
def find_fuzzy(root, name, roleName=None):
"""
Search root for any widget that contains the passed name/role strings.
"""
name_pattern = ".*%s.*" % name
role_pattern = None
if roleName:
role_pattern = ".*%s.*" % roleName
return DogtailApp.find_pattern(root, name_pattern, role_pattern)
@staticmethod
def print_nodes(root):
"""
Helper to print the entire node tree for the passed root. Useful
if to figure out the roleName for the object you are looking for
"""
def _walk(node):
try:
print "__str__=%s roleName=%s" % (str(node), node.roleName)
except Exception, e:
print "got exception: %s" % e
root.findChildren(_walk, isLambda=True)