uitests: console/keyring/viewer coverage work

Signed-off-by: Cole Robinson <crobinso@redhat.com>
This commit is contained in:
Cole Robinson
2020-08-29 13:23:34 -04:00
parent 2c86eefa5b
commit 64e3ff1d08
6 changed files with 114 additions and 38 deletions
+75 -4
View File
@@ -10,7 +10,7 @@ from virtinst import log
from tests.uitests import utils as uiutils from tests.uitests import utils as uiutils
def _vm_wrapper(vmname, uri="qemu:///system"): def _vm_wrapper(vmname, uri="qemu:///system", opts=None):
""" """
Decorator to define+start a VM and clean it up on exit Decorator to define+start a VM and clean it up on exit
""" """
@@ -25,7 +25,9 @@ def _vm_wrapper(vmname, uri="qemu:///system"):
dom.create() dom.create()
self.app.uri = uri self.app.uri = uri
self.conn = conn self.conn = conn
self.app.open(extra_opts=["--show-domain-console", vmname]) extra_opts = (opts or [])
extra_opts += ["--show-domain-console", vmname]
self.app.open(extra_opts=extra_opts)
fn(self, *args, **kwargs) fn(self, *args, **kwargs)
finally: finally:
try: try:
@@ -47,6 +49,7 @@ class Console(uiutils.UITestCase):
""" """
conn = None conn = None
extraopts = None
############## ##############
# Test cases # # Test cases #
@@ -101,6 +104,19 @@ class Console(uiutils.UITestCase):
win.find("Fullscreen Exit").click() win.find("Fullscreen Exit").click()
uiutils.check(lambda: win.size == newsize) uiutils.check(lambda: win.size == newsize)
# Tweak scaling
win.click_title()
win.click_title()
win.find("^View$", "menu").click()
scalemenu = win.find("Scale Display", "menu")
scalemenu.point()
scalemenu.find("Always", "radio menu item").click()
win.find("^View$", "menu").click()
scalemenu = win.find("Scale Display", "menu")
scalemenu.point()
scalemenu.find("Never", "radio menu item").click()
self.sleep(.5)
@_vm_wrapper("uitests-vnc-standard") @_vm_wrapper("uitests-vnc-standard")
def testConsoleVNCStandard(self): def testConsoleVNCStandard(self):
return self._checkConsoleStandard() return self._checkConsoleStandard()
@@ -123,12 +139,43 @@ class Console(uiutils.UITestCase):
passwd.typeText("xx") passwd.typeText("xx")
win.find("Login", "push button").click() win.find("Login", "push button").click()
self._click_alert_button("Viewer authentication error", "OK") self._click_alert_button("Viewer authentication error", "OK")
savecheck = win.find("Save this password", "check box")
if not savecheck.checked:
savecheck.click()
passwd.typeText("yy")
self.pressKey("Enter")
self._click_alert_button("Viewer authentication error", "OK")
# Check proper password # Check proper password
passwd.text = ""
passwd.typeText("goodp") passwd.typeText("goodp")
win.find("Login", "push button").click() win.find("Login", "push button").click()
uiutils.check(lambda: con.showing) uiutils.check(lambda: con.showing)
# Restart VM to retrigger console connect
smenu = win.find("Menu", "toggle button")
smenu.click()
smenu.find("Force Off", "menu item").click()
self._click_alert_button("you sure", "Yes")
win.find("Run", "push button").click()
uiutils.check(lambda: passwd.showing)
# Password should be filled in
uiutils.check(lambda: bool(passwd.text))
# Uncheck 'Save password' and login, which will delete it from keyring
savecheck.click()
win.find("Login", "push button").click()
uiutils.check(lambda: con.showing)
# Restart VM to retrigger console connect
smenu = win.find("Menu", "toggle button")
smenu.click()
smenu.find("Force Off", "menu item").click()
self._click_alert_button("you sure", "Yes")
win.find("Run", "push button").click()
uiutils.check(lambda: passwd.showing)
# Password should be empty now
uiutils.check(lambda: not bool(passwd.text))
@_vm_wrapper("uitests-vnc-password") @_vm_wrapper("uitests-vnc-password")
def testConsoleVNCPassword(self): def testConsoleVNCPassword(self):
return self._checkPassword() return self._checkPassword()
@@ -179,7 +226,7 @@ class Console(uiutils.UITestCase):
term = win.find("Serial Terminal") term = win.find("Serial Terminal")
uiutils.check(lambda: term.showing) uiutils.check(lambda: term.showing)
@_vm_wrapper("uitests-spice-specific") @_vm_wrapper("uitests-spice-specific", opts=["--test-options=spice-agent"])
def testConsoleSpiceSpecific(self): def testConsoleSpiceSpecific(self):
""" """
Spice specific behavior. Has lots of devices that will open Spice specific behavior. Has lots of devices that will open
@@ -193,8 +240,32 @@ class Console(uiutils.UITestCase):
# than that # than that
win.find("Virtual Machine", "menu").click() win.find("Virtual Machine", "menu").click()
win.find("Redirect USB", "menu item").click() win.find("Redirect USB", "menu item").click()
self.app.root.find("Select USB devices for redirection", "label")
usbwin = self.app.root.find("vmm dialog", "alert")
usbwin.find("Select USB devices for redirection", "label")
usbwin.find("SPICE CD", "check box").click()
chooser = self.app.root.find(None, "file chooser")
# Find the cwd bookmark on the left
chooser.find("virt-manager", "label").click()
chooser.find("virt-manager", "label").click()
chooser.find("COPYING").click()
self.pressKey("Enter")
uiutils.check(lambda: not chooser.showing)
usbwin.find("Close", "push button").click()
# Test fake guest resize behavior
def _click_auto():
vmenu = win.find("^View$", "menu")
vmenu.click()
smenu = vmenu.find("Scale Display", "menu")
smenu.point()
smenu.find("Auto resize VM", "check menu item").click()
_click_auto()
win.click_title()
win.click_title()
_click_auto()
win.click_title()
win.click_title()
def _testLiveHotplug(self, fname): def _testLiveHotplug(self, fname):
win = self.app.topwin win = self.app.topwin
+9 -11
View File
@@ -81,7 +81,7 @@ class _TimedRevealer(vmmGObject):
def _schedule_unreveal_timeout(self, timeout): def _schedule_unreveal_timeout(self, timeout):
if self._timeout_id: if self._timeout_id:
return return # pragma: no cover
def cb(): def cb():
self._revealer.set_reveal_child(False) self._revealer.set_reveal_child(False)
@@ -437,7 +437,7 @@ class vmmConsolePages(vmmGObjectUI):
def _resizeguest_ui_changed_cb(self, src): def _resizeguest_ui_changed_cb(self, src):
if not src.get_sensitive(): if not src.get_sensitive():
return return # pragma: no cover
val = int(self.widget("details-menu-view-resizeguest").get_active()) val = int(self.widget("details-menu-view-resizeguest").get_active())
self.vm.set_console_resizeguest(val) self.vm.set_console_resizeguest(val)
@@ -446,9 +446,9 @@ class vmmConsolePages(vmmGObjectUI):
def _do_size_to_vm(self, src_ignore): def _do_size_to_vm(self, src_ignore):
# Resize the console to best fit the VM resolution # Resize the console to best fit the VM resolution
if not self._viewer: if not self._viewer:
return return # pragma: no cover
if not self._viewer.console_get_desktop_resolution(): if not self._viewer.console_get_desktop_resolution():
return return # pragma: no cover
top_w, top_h = self.topwin.get_size() top_w, top_h = self.topwin.get_size()
viewer_alloc = self.widget("console-gfx-scroll").get_allocation() viewer_alloc = self.widget("console-gfx-scroll").get_allocation()
@@ -654,7 +654,7 @@ class vmmConsolePages(vmmGObjectUI):
if not self.vm: if not self.vm:
# This is triggered via cleanup + idle_add, so vm might # This is triggered via cleanup + idle_add, so vm might
# disappear and spam the logs # disappear and spam the logs
return return # pragma: no cover
pagenum = self.widget("console-pages").get_current_page() pagenum = self.widget("console-pages").get_current_page()
paused = self.vm.is_paused() paused = self.vm.is_paused()
@@ -690,7 +690,7 @@ class vmmConsolePages(vmmGObjectUI):
gdev = gdevs and gdevs[0] or None gdev = gdevs and gdevs[0] or None
if gdev: if gdev:
ginfo = ConnectionInfo(self.vm.conn, gdev) ginfo = ConnectionInfo(self.vm.conn, gdev)
except Exception as e: except Exception as e: # pragma: no cover
# We can fail here if VM is destroyed: xen is a bit racy # We can fail here if VM is destroyed: xen is a bit racy
# and can't handle domain lookups that soon after # and can't handle domain lookups that soon after
log.exception("Getting graphics console failed: %s", str(e)) log.exception("Getting graphics console failed: %s", str(e))
@@ -720,12 +720,10 @@ class vmmConsolePages(vmmGObjectUI):
if ginfo.gtype == "vnc": if ginfo.gtype == "vnc":
viewer_class = VNCViewer viewer_class = VNCViewer
elif ginfo.gtype == "spice": elif ginfo.gtype == "spice":
if have_spice_gtk: if not have_spice_gtk: # pragma: no cover
viewer_class = SpiceViewer
else:
raise RuntimeError("Error opening Spice console, " raise RuntimeError("Error opening Spice console, "
"SpiceClientGtk missing") "SpiceClientGtk missing")
viewer_class = SpiceViewer
self._viewer = viewer_class(self.vm, ginfo) self._viewer = viewer_class(self.vm, ginfo)
self._connect_viewer_signals() self._connect_viewer_signals()
@@ -880,7 +878,7 @@ class vmmConsolePages(vmmGObjectUI):
menu = self.widget("details-menu-view-serial-list").get_submenu() menu = self.widget("details-menu-view-serial-list").get_submenu()
for child in menu.get_children(): for child in menu.get_children():
if isinstance(child, Gtk.SeparatorMenuItem): if isinstance(child, Gtk.SeparatorMenuItem):
break break # pragma: no cover
if child.get_sensitive(): if child.get_sensitive():
child.toggled() child.toggled()
break break
+8 -3
View File
@@ -380,8 +380,10 @@ class VNCViewer(Viewer):
return self._display.is_open() return self._display.is_open()
def _get_scaling(self): def _get_scaling(self):
if self._display:
return self._display.get_scaling() return self._display.get_scaling()
def _set_scaling(self, scaling): def _set_scaling(self, scaling):
if self._display:
return self._display.set_scaling(scaling) return self._display.set_scaling(scaling)
def _get_grab_keys(self): def _get_grab_keys(self):
@@ -431,11 +433,11 @@ class VNCViewer(Viewer):
return False return False
def _get_usb_widget(self): def _get_usb_widget(self):
return None return None # pragma: no cover
def _has_usb_redirection(self): def _has_usb_redirection(self):
return False return False
def _has_agent(self): def _has_agent(self):
return False return False # pragma: no cover
####################### #######################
@@ -696,7 +698,8 @@ class SpiceViewer(Viewer):
def _has_agent(self): def _has_agent(self):
if not self._main_channel: if not self._main_channel:
return False return False
return self._main_channel.get_property("agent-connected") return (self._main_channel.get_property("agent-connected") or
self.config.CLITestOptions.spice_agent)
def _open_host(self): def _open_host(self):
host, port, tlsport = self._ginfo.get_conn_host() host, port, tlsport = self._ginfo.get_conn_host()
@@ -727,8 +730,10 @@ class SpiceViewer(Viewer):
self._spice_session.connect() self._spice_session.connect()
def _get_scaling(self): def _get_scaling(self):
if self._display:
return self._display.get_property("scaling") return self._display.get_property("scaling")
def _set_scaling(self, scaling): def _set_scaling(self, scaling):
if self._display:
self._display.set_property("scaling", scaling) self._display.set_property("scaling", scaling)
def _set_resizeguest(self, val): def _set_resizeguest(self, val):
+5 -6
View File
@@ -234,7 +234,7 @@ class vmmEngine(vmmGObject):
We serialize conn autostart, so polkit/ssh-askpass doesn't spam We serialize conn autostart, so polkit/ssh-askpass doesn't spam
""" """
if self._exiting: if self._exiting:
return return # pragma: no cover
connections_queue = queue.Queue() connections_queue = queue.Queue()
auto_conns = [conn.get_uri() for conn in self._connobjs.values() if auto_conns = [conn.get_uri() for conn in self._connobjs.values() if
@@ -366,7 +366,7 @@ class vmmEngine(vmmGObject):
ignore1, ignore2, conn, kwargs = self._tick_queue.get() ignore1, ignore2, conn, kwargs = self._tick_queue.get()
try: try:
conn.tick_from_engine(**kwargs) conn.tick_from_engine(**kwargs)
except Exception: except Exception: # pragma: no cover
# Don't attempt to show any UI error here, since it # Don't attempt to show any UI error here, since it
# can cause dialogs to appear from nowhere if say # can cause dialogs to appear from nowhere if say
# libvirtd is shut down # libvirtd is shut down
@@ -376,7 +376,6 @@ class vmmEngine(vmmGObject):
# Need to clear reference to make leak check happy # Need to clear reference to make leak check happy
conn = None conn = None
self._tick_queue.task_done() self._tick_queue.task_done()
return 1
##################################### #####################################
@@ -423,7 +422,7 @@ class vmmEngine(vmmGObject):
Public call, manager/details/... use this to force exit the app Public call, manager/details/... use this to force exit the app
""" """
if self._exiting: if self._exiting:
return return # pragma: no cover
self._exiting = True self._exiting = True
@@ -438,7 +437,7 @@ class vmmEngine(vmmGObject):
# Engine will always appear to leak # Engine will always appear to leak
objs.remove(self.object_key) objs.remove(self.object_key)
for name in objs: for name in objs: # pragma: no cover
log.debug("LEAK: %s", name) log.debug("LEAK: %s", name)
log.debug("Exiting app normally.") log.debug("Exiting app normally.")
@@ -516,7 +515,7 @@ class vmmEngine(vmmGObject):
self.CLI_SHOW_DOMAIN_CONSOLE, self.CLI_SHOW_DOMAIN_CONSOLE,
self.CLI_SHOW_DOMAIN_DELETE]): self.CLI_SHOW_DOMAIN_DELETE]):
self._cli_show_vm_helper(uri, clistr, show_window) self._cli_show_vm_helper(uri, clistr, show_window)
else: else: # pragma: no cover
raise RuntimeError("Unknown cli window command '%s'" % raise RuntimeError("Unknown cli window command '%s'" %
show_window) show_window)
+10 -10
View File
@@ -55,11 +55,11 @@ class vmmKeyring(vmmGObject):
"org.freedesktop.Secret.Collection", None) "org.freedesktop.Secret.Collection", None)
log.debug("Using keyring session %s", self._session) log.debug("Using keyring session %s", self._session)
except Exception: except Exception: # pragma: no cover
log.exception("Error determining keyring") log.exception("Error determining keyring")
def _cleanup(self): def _cleanup(self):
pass pass # pragma: no cover
def _add_secret(self, secret): def _add_secret(self, secret):
ret = None ret = None
@@ -76,7 +76,7 @@ class vmmKeyring(vmmGObject):
_id = self._collection.CreateItem("(a{sv}(oayays)b)", _id = self._collection.CreateItem("(a{sv}(oayays)b)",
props, params, replace)[0] props, params, replace)[0]
ret = int(_id.rsplit("/")[-1]) ret = int(_id.rsplit("/")[-1])
except Exception: except Exception: # pragma: no cover
log.exception("Failed to add keyring secret") log.exception("Failed to add keyring secret")
return ret return ret
@@ -112,7 +112,7 @@ class vmmKeyring(vmmGObject):
attrs["%s" % key] = "%s" % val attrs["%s" % key] = "%s" % val
ret = _vmmSecret(label, secret, attrs) ret = _vmmSecret(label, secret, attrs)
except Exception: except Exception: # pragma: no cover
log.exception("Failed to get keyring secret id=%s", _id) log.exception("Failed to get keyring secret id=%s", _id)
return ret return ret
@@ -130,7 +130,7 @@ class vmmKeyring(vmmGObject):
def get_console_password(self, vm): def get_console_password(self, vm):
if not self.is_available(): if not self.is_available():
return ("", "") return ("", "") # pragma: no cover
username, keyid = vm.get_console_password() username, keyid = vm.get_console_password()
@@ -139,30 +139,30 @@ class vmmKeyring(vmmGObject):
secret = self._get_secret(keyid) secret = self._get_secret(keyid)
if secret is None or secret.get_name() != self._get_secret_name(vm): if secret is None or secret.get_name() != self._get_secret_name(vm):
return ("", "") return ("", "") # pragma: no cover
if (secret.attributes.get("hvuri", None) != vm.conn.get_uri() or if (secret.attributes.get("hvuri", None) != vm.conn.get_uri() or
secret.attributes.get("uuid", None) != vm.get_uuid()): secret.attributes.get("uuid", None) != vm.get_uuid()):
return ("", "") return ("", "") # pragma: no cover
return (secret.get_secret(), username or "") return (secret.get_secret(), username or "")
def set_console_password(self, vm, password, username=""): def set_console_password(self, vm, password, username=""):
if not self.is_available(): if not self.is_available():
return return # pragma: no cover
secret = _vmmSecret(self._get_secret_name(vm), password, secret = _vmmSecret(self._get_secret_name(vm), password,
{"uuid": vm.get_uuid(), {"uuid": vm.get_uuid(),
"hvuri": vm.conn.get_uri()}) "hvuri": vm.conn.get_uri()})
keyid = self._add_secret(secret) keyid = self._add_secret(secret)
if keyid is None: if keyid is None:
return return # pragma: no cover
vm.set_console_password(username, keyid) vm.set_console_password(username, keyid)
def del_console_password(self, vm): def del_console_password(self, vm):
if not self.is_available(): if not self.is_available():
return return # pragma: no cover
ignore, keyid = vm.get_console_password() ignore, keyid = vm.get_console_password()
if keyid == -1: if keyid == -1:
+3
View File
@@ -95,6 +95,8 @@ class CLITestOptionsClass:
managed save issues managed save issues
* test-vm-run-fail: Make VM run fail, so we can test the error path * test-vm-run-fail: Make VM run fail, so we can test the error path
* spice-agent: Make spice-agent detection return true in viewer.py
""" """
def __init__(self, test_options_str, test_first_run): def __init__(self, test_options_str, test_first_run):
optset = set() optset = set()
@@ -128,6 +130,7 @@ class CLITestOptionsClass:
self.config_libguestfs = _get("config-libguestfs") self.config_libguestfs = _get("config-libguestfs")
self.test_managed_save = _get("test-managed-save") self.test_managed_save = _get("test-managed-save")
self.test_vm_run_fail = _get("test-vm-run-fail") self.test_vm_run_fail = _get("test-vm-run-fail")
self.spice_agent = _get("spice-agent")
if optset: # pragma: no cover if optset: # pragma: no cover
raise RuntimeError("Unknown --test-options keys: %s" % optset) raise RuntimeError("Unknown --test-options keys: %s" % optset)