mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-02-25 18:55:27 -06:00
tests: Add test_ui target for dogtail UI tests, add an example
This commit is contained in:
57
setup.py
57
setup.py
@@ -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
26
tests/uitests/__init__.py
Normal 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
63
tests/uitests/newvm.py
Normal 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
74
tests/uitests/utils.py
Normal 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)
|
||||
Reference in New Issue
Block a user