From 8ce83dbc53f4363b540449da1e359b39f49b34d1 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Fri, 18 Sep 2020 17:27:50 -0400 Subject: [PATCH] uitests: big mess of work * Convert to pytest style functions * Move lots of shared code to our App class * Reduce dogtail sleep amounts to speed up the whole testsuite * Improve robustness in a lot of areas Signed-off-by: Cole Robinson --- tests/uitests/lib/__init__.py | 1 - tests/uitests/lib/_dogtailinit.py | 6 +- tests/uitests/lib/_node.py | 36 +- tests/uitests/lib/app.py | 173 +- tests/uitests/lib/testcase.py | 16 - tests/uitests/test_addhardware.py | 1403 ++++++++-------- tests/uitests/test_cli.py | 347 ++-- tests/uitests/test_clonevm.py | 417 +++-- tests/uitests/test_connection.py | 180 +-- tests/uitests/test_createconn.py | 208 +-- tests/uitests/test_createnet.py | 297 ++-- tests/uitests/test_createpool.py | 289 ++-- tests/uitests/test_createvm.py | 2518 +++++++++++++++-------------- tests/uitests/test_createvol.py | 280 ++-- tests/uitests/test_delete.py | 471 +++--- tests/uitests/test_details.py | 1719 ++++++++++---------- tests/uitests/test_host.py | 312 ++-- tests/uitests/test_inspection.py | 86 +- tests/uitests/test_livetests.py | 751 ++++----- tests/uitests/test_manager.py | 633 ++++---- tests/uitests/test_mediachange.py | 187 +-- tests/uitests/test_migrate.py | 256 ++- tests/uitests/test_prefs.py | 259 ++- tests/uitests/test_snapshot.py | 324 ++-- tests/uitests/test_systray.py | 187 ++- virtManager/connection.py | 2 +- virtManager/object/domain.py | 4 +- 27 files changed, 5712 insertions(+), 5650 deletions(-) delete mode 100644 tests/uitests/lib/testcase.py diff --git a/tests/uitests/lib/__init__.py b/tests/uitests/lib/__init__.py index a7218318..668a0822 100644 --- a/tests/uitests/lib/__init__.py +++ b/tests/uitests/lib/__init__.py @@ -5,4 +5,3 @@ from . import _dogtailinit from . import _node from . import app from . import utils -from . import testcase diff --git a/tests/uitests/lib/_dogtailinit.py b/tests/uitests/lib/_dogtailinit.py index 57464c0d..89edc355 100644 --- a/tests/uitests/lib/_dogtailinit.py +++ b/tests/uitests/lib/_dogtailinit.py @@ -16,11 +16,13 @@ gi.require_version('Atspi', '2.0') import dogtail.config import dogtail.utils -# Perform 5 search attempts if a widget lookup fails (default 20) -dogtail.config.config.searchCutoffCount = 5 +# find() backoff handling +dogtail.config.config.searchBackoffDuration = .1 +dogtail.config.config.searchCutoffCount = 20 # Use .1 second delay between each action (default 1) dogtail.config.config.actionDelay = .1 +dogtail.config.config.defaultDelay = .1 # Turn off needlessly noisy debugging DOGTAIL_DEBUG = False diff --git a/tests/uitests/lib/_node.py b/tests/uitests/lib/_node.py index 8cdb1b8d..2d888587 100644 --- a/tests/uitests/lib/_node.py +++ b/tests/uitests/lib/_node.py @@ -210,7 +210,17 @@ class _VMMDogtailNode(dogtail.tree.Node): # pylint: disable=arguments-differ,signature-differs self.check_onscreen() self.check_sensitive() - dogtail.tree.Node.click(self, *args, **kwargs) + super().click(*args, **kwargs) + + def point(self, *args, **kwargs): + # pylint: disable=signature-differs + super().point(*args, **kwargs) + + if (self.roleName == "menu" and + self.accessible_parent.roleName == "menu"): + # Widget is a submenu, make sure the item is in selected + # state before we return + utils.check(lambda: self.state_selected) def set_text(self, text): self.check_onscreen() @@ -231,12 +241,29 @@ class _VMMDogtailNode(dogtail.tree.Node): raise RuntimeError("Could not bring widget on screen") return self + def window_maximize(self): + assert self.roleName in ["frame", "dialog"] + utils.check(lambda: self.active) + self.click_title() + s1 = self.size + self.keyCombo("F10") + utils.check(lambda: self.size != s1) + self.grabFocus() + + def window_close(self): + assert self.roleName in ["frame", "alert", "dialog"] + self.click_title() + utils.check(lambda: self.active) + self.keyCombo("F4") + utils.check(lambda: not self.showing) + ######################### # Widget search helpers # ######################### - def find(self, name, roleName=None, labeller_text=None, check_active=True): + def find(self, name, roleName=None, labeller_text=None, + check_active=True, recursive=True): """ Search root for any widget that contains the passed name/role regex strings. @@ -244,7 +271,7 @@ class _VMMDogtailNode(dogtail.tree.Node): pred = _FuzzyPredicate(name, roleName, labeller_text) try: - ret = self.findChild(pred) + ret = self.findChild(pred, recursive=recursive) except dogtail.tree.SearchError: raise dogtail.tree.SearchError("Didn't find widget with name='%s' " "roleName='%s' labeller_text='%s'" % @@ -325,6 +352,9 @@ class _VMMDogtailNode(dogtail.tree.Node): """ print(self.fmt_nodes()) + def print_states(self): + print([s.value_nick for s in self.getState().get_states()]) + # This is the same hack dogtail uses to extend the Accessible class. _bases = list(pyatspi.Accessibility.Accessible.__bases__) diff --git a/tests/uitests/lib/app.py b/tests/uitests/lib/app.py index 9b82525b..8d83387a 100644 --- a/tests/uitests/lib/app.py +++ b/tests/uitests/lib/app.py @@ -24,6 +24,7 @@ class VMMDogtailApp(object): self._proc = None self._root = None self._topwin = None + self._manager = None self.uri = uri @@ -37,6 +38,11 @@ class VMMDogtailApp(object): def sleep(self, *args, **kwargs): return time.sleep(*args, **kwargs) + def find_window(self, name, roleName=None): + if roleName is None: + roleName = "(frame|dialog|alert|window)" + return self.root.find(name=name, roleName=roleName, recursive=False) + rawinput = dogtail.rawinput tree = dogtail.tree @@ -45,27 +51,16 @@ class VMMDogtailApp(object): # virt-manager specific helpers # ################################# - def open_host_window(self, tab, conn_label="test testdriver.xml"): - """ - Helper to open host connection window and switch to a tab - """ - self.root.find_fuzzy(conn_label, "table cell").click() - self.root.find_fuzzy("Edit", "menu").click() - self.root.find_fuzzy("Connection Details", "menu item").click() - win = self.root.find_fuzzy( - "%s - Connection Details" % conn_label, "frame") - win.find_fuzzy(tab, "page tab").click() - return win + def get_manager(self): + if not self._manager: + self._manager = self.find_window("Virtual Machine Manager") + return self._manager - def open_details_window(self, vmname, shutdown=False, double=False): - if double: - self.root.find_fuzzy(vmname, "table cell").doubleClick() - else: - self.root.find_fuzzy(vmname, "table cell").click(button=3) - self.root.find("Open", "menu item").click() - - win = self.root.find("%s on" % vmname, "frame") - win.find("Details", "radio button").click() + def find_details_window(self, vmname, + click_details=False, shutdown=False): + win = self.find_window("%s on" % vmname, "frame") + if click_details: + win.find("Details", "radio button").click() if shutdown: win.find("Shut Down", "push button").click() run = win.find("Run", "push button") @@ -73,13 +68,13 @@ class VMMDogtailApp(object): return win def click_alert_button(self, label_text, button_text): - alert = self.root.find("vmm dialog", "alert") + alert = self.find_window("vmm dialog", "alert") alert.find_fuzzy(label_text, "label") alert.find(button_text, "push button").click() utils.check(lambda: not alert.active) def select_storagebrowser_volume(self, pool, vol, doubleclick=False): - browsewin = self.root.find("vmm-storage-browser") + browsewin = self.find_window("vmm-storage-browser") browsewin.find_fuzzy(pool, "table cell").click() volcell = browsewin.find_fuzzy(vol, "table cell") if doubleclick: @@ -90,6 +85,134 @@ class VMMDogtailApp(object): utils.check(lambda: not browsewin.active) + ########################## + # manager window helpers # + ########################## + + def manager_open_createconn(self): + manager = self.get_manager() + manager.find("File", "menu").click() + manager.find("Add Connection...", "menu item").click() + win = self.root.find("Add Connection", "dialog") + return win + + def manager_createconn(self, uri): + win = self.manager_open_createconn() + win.combo_select("Hypervisor", "Custom URI") + win.find("uri-entry", "text").set_text(uri) + win.find("Connect", "push button").click() + utils.check(lambda: win.showing is False) + + def manager_get_conn_cell(self, conn_label): + return self.get_manager().find(conn_label, "table cell") + + def manager_conn_connect(self, conn_label): + c = self.manager_get_conn_cell(conn_label) + c.click(button=3) + self.root.find("conn-connect", "menu item").click() + utils.check(lambda: "Not Connected" not in c.text) + return c + + def manager_conn_disconnect(self, conn_label): + c = self.manager_get_conn_cell(conn_label) + c.click(button=3) + self.root.find("conn-disconnect", "menu item").click() + utils.check(lambda: "Not Connected" in c.text) + return c + + def manager_conn_delete(self, conn_label): + c = self.manager_get_conn_cell(conn_label) + c.click(button=3) + self.root.find("conn-delete", "menu item").click() + self.click_alert_button("will remove the connection", "Yes") + utils.check(lambda: c.dead) + + def manager_vm_action(self, vmname, confirm_click_no=False, + run=False, shutdown=False, destroy=False, reset=False, + reboot=False, pause=False, resume=False, save=False, + restore=False, clone=False, migrate=False, delete=False, + details=False): + manager = self.get_manager() + vmcell = manager.find(vmname + "\n", "table cell") + + if run: + action = "Run" + if shutdown: + action = "Shut Down" + if reboot: + action = "Reboot" + if reset: + action = "Force Reset" + if destroy: + action = "Force Off" + if pause: + action = "Pause" + if resume: + action = "Resume" + if save: + action = "Save" + if restore: + action = "Restore" + if clone: + action = "Clone" + if migrate: + action = "Migrate" + if delete: + action = "Delete" + if details: + action = "Open" + + needs_shutdown = shutdown or destroy or reset or reboot or save + needs_confirm = needs_shutdown or pause + + def _do_click(): + vmcell.click() + vmcell.click(button=3) + menu = self.root.find("vm-action-menu") + utils.check(lambda: menu.onscreen) + if needs_shutdown: + smenu = menu.find("Shut Down", "menu") + smenu.point() + utils.check(lambda: smenu.onscreen) + item = smenu.find(action, "menu item") + else: + item = menu.find(action, "menu item") + utils.check(lambda: item.onscreen) + item.point() + utils.check(lambda: item.state_selected) + item.click() + return menu + + m = _do_click() + if needs_confirm: + if confirm_click_no: + self.click_alert_button("Are you sure", "No") + m = _do_click() + self.click_alert_button("Are you sure", "Yes") + utils.check(lambda: not m.onscreen) + + def manager_open_clone(self, vmname): + self.manager_vm_action(vmname, clone=True) + return self.find_window("Clone Virtual Machine") + + def manager_open_details(self, vmname, shutdown=False): + self.manager_vm_action(vmname, details=True) + win = self.find_details_window(vmname, + shutdown=shutdown, click_details=True) + return win + + def manager_open_host(self, tab, conn_label="test testdriver.xml"): + """ + Helper to open host connection window and switch to a tab + """ + self.root.find_fuzzy(conn_label, "table cell").click() + self.root.find_fuzzy("Edit", "menu").click() + self.root.find_fuzzy("Connection Details", "menu item").click() + win = self.find_window("%s - Connection Details" % conn_label) + win.find_fuzzy(tab, "page tab").click() + return win + + ########################### # Process management APIs # ########################### @@ -156,7 +279,7 @@ class VMMDogtailApp(object): window_name=None, xmleditor_enabled=False, keyfile=None, break_setfacl=False, first_run=True, no_fork=True, will_fail=False, enable_libguestfs=False, - firstrun_uri=None): + firstrun_uri=None, show_console=None): extra_opts = extra_opts or [] uri = uri or self.uri @@ -174,6 +297,8 @@ class VMMDogtailApp(object): cmd += ["--no-fork"] if use_uri: cmd += ["--connect", uri] + if show_console: + cmd += ["--show-domain-console=%s" % show_console] if first_run: cmd.append("--test-options=first-run") @@ -205,4 +330,4 @@ class VMMDogtailApp(object): self._proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr) if not will_fail: self._root = dogtail.tree.root.application("virt-manager") - self._topwin = self._root.find(window_name, "(frame|dialog|alert)") + self._topwin = self.find_window(window_name) diff --git a/tests/uitests/lib/testcase.py b/tests/uitests/lib/testcase.py deleted file mode 100644 index fdb17dea..00000000 --- a/tests/uitests/lib/testcase.py +++ /dev/null @@ -1,16 +0,0 @@ -# This work is licensed under the GNU GPLv2 or later. -# See the COPYING file in the top-level directory. - -import unittest - -from .app import VMMDogtailApp - - -class UITestCase(unittest.TestCase): - """ - Common testcase bits shared for ui tests - """ - def setUp(self): - self.app = VMMDogtailApp() - def tearDown(self): - self.app.stop() diff --git a/tests/uitests/test_addhardware.py b/tests/uitests/test_addhardware.py index 83c5486c..25af077d 100644 --- a/tests/uitests/test_addhardware.py +++ b/tests/uitests/test_addhardware.py @@ -12,7 +12,7 @@ def _search_permissions_decorator(fn): """ Decorator to set up necessary bits to test disk permission search """ - def wrapper(self, *args, **kwargs): + def wrapper(app, *args, **kwargs): # Generate capabilities XML from a template, with out # UID/GID inserted as the intended emulator permissions capsfile = (tests.utils.UITESTDATADIR + @@ -34,702 +34,719 @@ def _search_permissions_decorator(fn): tmpdir = tmpobj.name try: os.chmod(tmpdir, 0o000) - fn(self, uri, tmpdir, *args, **kwargs) + fn(app, uri, tmpdir, *args, **kwargs) finally: os.chmod(tmpdir, 0o777) return wrapper -class AddHardware(lib.testcase.UITestCase): +def _select_hw(addhw, hwname, tabname): + addhw.find(hwname, "table cell").click() + tab = addhw.find(tabname, None) + lib.utils.check(lambda: tab.showing) + return tab + + +def _finish(addhw, check): + addhw.find("Finish", "push button").click() + lib.utils.check(lambda: not addhw.active) + if check: + lib.utils.check(lambda: check.active) + + +def _open_addhw(app, details): + details.find("add-hardware", "push button").click() + addhw = app.find_window("Add New Virtual Hardware") + return addhw + + +def _open_app(app, vmname, title=None, shutdown=False, **kwargs): + app.open(show_console=vmname, **kwargs) + details = app.find_details_window(title or vmname, + click_details=True, shutdown=shutdown) + return details + + +############## +# Test cases # +############## + + +def testAddControllers(app): """ - UI tests for virt-manager's VM addhardware window + Add various controller configs """ - - ################### - # Private helpers # - ################### - - def _open_addhw_window(self, details): - details.find("add-hardware", "push button").click() - addhw = self.app.root.find("Add New Virtual Hardware", "frame") - return addhw - - def _select_hw(self, addhw, hwname, tabname): - addhw.find(hwname, "table cell").click() - tab = addhw.find(tabname, None) - lib.utils.check(lambda: tab.showing) - return tab - - def _finish(self, addhw, check): - addhw.find("Finish", "push button").click() - lib.utils.check(lambda: not addhw.active) - if check: - lib.utils.check(lambda: check.active) - - - ############## - # Test cases # - ############## - - def testAddControllers(self): - """ - Add various controller configs - """ - details = self.app.open_details_window("test-clone-simple") - addhw = self._open_addhw_window(details) - - # Default SCSI - tab = self._select_hw(addhw, "Controller", "controller-tab") - tab.combo_select("Type:", "SCSI") - self._finish(addhw, check=details) - - # Virtio SCSI - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Controller", "controller-tab") - tab.combo_select("Type:", "SCSI") - tab.combo_select("Model:", "VirtIO SCSI") - self._finish(addhw, check=details) - - # USB 2 - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Controller", "controller-tab") - tab.combo_select("Type:", "USB") - tab.combo_select("Model:", "USB 2") - self._finish(addhw, check=details) - - # USB 3 - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Controller", "controller-tab") - tab.combo_select("Type:", "USB") - tab.combo_select("Model:", "USB 3") - # Can't add more than 1 USB controller, so finish isn't sensitive - finish = addhw.find("Finish", "push button") - lib.utils.check(lambda: not finish.sensitive) - - def testAddCephDisk(self): - """ - Add a disk with a ceph volume, ensure it maps correctly - """ - details = self.app.open_details_window("test-clone-simple") - addhw = self._open_addhw_window(details) - - # Select ceph volume for disk - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.find_fuzzy("Select or create", "radio").click() - tab.find("storage-browse", "push button").click() - browse = self.app.root.find("vmm-storage-browser") - browse.find_fuzzy("rbd-ceph", "table cell").bring_on_screen().click() - browse.find_fuzzy("some-rbd-vol", "table cell").click() - browse.find("Choose Volume", "push button").click() - self._finish(addhw, check=details) - - # Check disk details, make sure it correctly selected volume - details.find("IDE Disk 2", "table cell").click() - tab = details.find("disk-tab") - lib.utils.check(lambda: tab.showing) - disk_path = tab.find("disk-source-path") - lib.utils.check(lambda: "rbd://" in disk_path.text) - - def testAddDisks(self): - """ - Add various disk configs and test storage browser - """ - details = self.app.open_details_window("test-clone-simple") - addhw = self._open_addhw_window(details) - - # Default disk - tab = self._select_hw(addhw, "Storage", "storage-tab") - self._finish(addhw, check=details) - - # Disk with some tweaks - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.combo_select("Bus type:", "VirtIO") - tab.find("Advanced options", "toggle button").click_expander() - tab.find("Shareable:", "check box").click() - tab.find("Readonly:", "check box").click() - tab.find("Serial:", "text").set_text("ZZZZ") - tab.combo_select("Cache mode:", "none") - tab.combo_select("Discard mode:", "ignore") - tab.combo_select("Detect zeroes:", "unmap") - # Size too big - tab.find("GiB", "spin button").set_text("2000") - self._finish(addhw, check=None) - self.app.click_alert_button("not enough free space", "Close") - tab.find("GiB", "spin button").set_text("1.5") - self._finish(addhw, check=details) - - # USB disk with removable setting - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.combo_select("Bus type:", "USB") - tab.find("Advanced options", "toggle button").click_expander() - tab.find("Removable:", "check box").click() - self._finish(addhw, check=details) - - # Managed storage tests - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.find_fuzzy("Select or create", "radio").click() - self._finish(addhw, check=None) - self.app.click_alert_button("storage path must be specified", "OK") - tab.find("storage-browse", "push button").click() - browse = self.app.root.find("vmm-storage-browser") - - # Create a vol, refresh, then delete it - browse.find_fuzzy("default-pool", "table cell").click() - browse.find("vol-new", "push button").click() - newvol = self.app.root.find("Add a Storage Volume", "frame") - newname = "a-newvol" - newvol.find("Name:", "text").set_text(newname) - newvol.find("Finish", "push button").click() - lib.utils.check(lambda: not newvol.showing) - volcell = browse.find(newname, "table cell") - lib.utils.check(lambda: volcell.selected) - browse.find("vol-refresh", "push button").click() - volcell = browse.find(newname, "table cell") - lib.utils.check(lambda: volcell.selected) - browse.find("vol-delete", "push button").click() - self.app.click_alert_button("permanently delete the volume", "Yes") - lib.utils.check(lambda: volcell.dead) - - # Test browse local - browse.find("Browse Local", "push button").click() - chooser = self.app.root.find( - "Locate existing storage", "file chooser") - - # use filename that is near the beginning of the file list when sorted, - # as the row in the file dialog may become scrolled out of the view and - # cause the test to fail - fname = "COPYING" - chooser.find(fname, "table cell").click() - chooser.find("Open", "push button").click() - lib.utils.check(lambda: not chooser.showing) - lib.utils.check(lambda: addhw.active) - storageent = tab.find("storage-entry") - lib.utils.check(lambda: ("/" + fname) in storageent.text) - - # Reopen dialog, select a volume, etic - tab.find("storage-browse", "push button").click() - browse = self.app.root.find("vmm-storage-browser") - - browse.find_fuzzy("disk-pool", "table cell").click() - browse.find("diskvol1", "table cell").click() - browse.find("Choose Volume", "push button").click() - lib.utils.check(lambda: "/diskvol1" in storageent.text) - self._finish(addhw, check=None) - self.app.click_alert_button("already in use by", "No") - self._finish(addhw, check=None) - self.app.click_alert_button("already in use by", "Yes") - lib.utils.check(lambda: details.active) - - - # choose file for floppy - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.combo_select("Device type:", "Floppy device") - diskradio = tab.find_fuzzy("Create a disk image", "radio") - lib.utils.check(lambda: not diskradio.sensitive) - tab.find("storage-entry").set_text("/dev/default-pool/bochs-vol") - self._finish(addhw, check=details) - - # empty cdrom - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.combo_select("Device type:", "CDROM device") - tab.combo_select("Bus type:", "SCSI") - self._finish(addhw, check=details) - - @_search_permissions_decorator - def testAddDiskSearchPermsCheckbox(self, uri, tmpdir): - """ - Test search permissions 'no' and checkbox case - """ - self.app.uri = uri - details = self.app.open_details_window("test-clone-simple") - - # Say 'No' but path should still work due to test driver - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.find_fuzzy("Select or create", "radio").click() - path = tmpdir + "/foo1.img" - tab.find("storage-entry").set_text(path) - self._finish(addhw, check=None) - self.app.click_alert_button("emulator may not have", "No") - lib.utils.check(lambda: details.active) - - # Say 'don't ask again' - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.find_fuzzy("Select or create", "radio").click() - path = tmpdir + "/foo2.img" - tab.find("storage-entry").set_text(path) - self._finish(addhw, check=None) - alert = self.app.root.find_fuzzy("vmm dialog", "alert") - alert.find_fuzzy("Don't ask", "check box").click() - self.app.click_alert_button("emulator may not have", "No") - lib.utils.check(lambda: details.active) - - # Confirm it doesn't ask about path again - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.find_fuzzy("Select or create", "radio").click() - path = tmpdir + "/foo3.img" - tab.find("storage-entry").set_text(path) - self._finish(addhw, check=details) - - @_search_permissions_decorator - def testAddDiskSearchPermsSuccess(self, uri, tmpdir): - """ - Select 'Yes' for search perms fixing - """ - self.app.uri = uri - details = self.app.open_details_window("test-clone-simple") - - # Say 'Yes' - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.find_fuzzy("Select or create", "radio").click() - path = tmpdir + "/foo1.img" - tab.find("storage-entry").set_text(path) - self._finish(addhw, check=None) - self.app.click_alert_button("emulator may not have", "Yes") - lib.utils.check(lambda: details.active) - - # Confirm it doesn't ask about path again - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.find_fuzzy("Select or create", "radio").click() - path = tmpdir + "/foo3.img" - tab.find("storage-entry").set_text(path) - self._finish(addhw, check=details) - - @_search_permissions_decorator - def testAddDiskSearchPermsFail(self, uri, tmpdir): - """ - Force perms fixing to fail - """ - self.app.uri = uri - self.app.open(break_setfacl=True) - details = self.app.open_details_window("test-clone-simple") - - # Say 'Yes' and it should fail, then blacklist the paths - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.find_fuzzy("Select or create", "radio").click() - path = tmpdir + "/foo1.img" - tab.find("storage-entry").set_text(path) - self._finish(addhw, check=None) - self.app.click_alert_button("emulator may not have", "Yes") - alert = self.app.root.find("vmm dialog", "alert") - alert.find_fuzzy("Errors were encountered", "label") - alert.find_fuzzy("Don't ask", "check box").click() - alert.find_fuzzy("OK", "push button").click() - lib.utils.check(lambda: details.active) - - # Confirm it doesn't ask about path again - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Storage", "storage-tab") - tab.find_fuzzy("Select or create", "radio").click() - path = tmpdir + "/foo2.img" - tab.find("storage-entry").set_text(path) - self._finish(addhw, check=details) - - def testAddNetworks(self): - """ - Test various network configs - """ - details = self.app.open_details_window("test-clone-simple") - addhw = self._open_addhw_window(details) - - # Basic network + opts - tab = self._select_hw(addhw, "Network", "network-tab") - tab.combo_select("net-source", "Virtual network 'default'") - tab.find("MAC Address Field", "text").set_text("00:11:00:11:00:11") - tab.combo_select("Device model:", "virtio") - self._finish(addhw, check=details) - - # Manual macvtap - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Network", "network-tab") - tab.combo_select("net-source", "Macvtap device...") - tab.find("Device name:", "text").set_text("macvtapfoo7") - self._finish(addhw, check=details) - - # Manual bridge. Also trigger MAC collision - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Network", "network-tab") - tab.find("mac-address-enable", "check box").click() - tab.combo_select("net-source", "Bridge device...") - tab.find("Device name:", "text").set_text("zbr0") - self._finish(addhw, check=None) - # Check MAC validation error - self.app.click_alert_button("00:11:22:33:44:55", "Close") - - # Fix MAC - tab.find("mac-address-enable", "check box").click() - tab.find("MAC Address Field", "text").set_text("00:11:0A:11:00:11") - self._finish(addhw, check=details) - - - def testAddGraphics(self): - """ - Graphics device testing - """ - details = self.app.open_details_window("test-clone-simple") - addhw = self._open_addhw_window(details) - - # VNC example - tab = self._select_hw(addhw, "Graphics", "graphics-tab") - tab.combo_select("Type:", "VNC") - tab.combo_select("Listen type:", "Address") - tab.combo_select("Address:", "All interfaces") - tab.find("graphics-port-auto", "check").click() - tab.find("graphics-port", "spin button").set_text("1234") - tab.find("Password:", "check").click() - passwd = tab.find_fuzzy("graphics-password", "text") - newpass = "foobar" - passwd.typeText(newpass) - tab.find("Show password", "check").click() - lib.utils.check(lambda: passwd.text == newpass) - tab.find("Show password", "check").click() - lib.utils.check(lambda: passwd.text != newpass) - self._finish(addhw, check=None) - # Catch a port error - self.app.click_alert_button("Port must be above 5900", "Close") - tab.find("graphics-port", "spin button").set_text("5920") - self._finish(addhw, check=details) - - # Spice regular example - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Graphics", "graphics-tab") - tab.combo_select("Type:", "Spice") - self._finish(addhw, check=details) - - # Spice GL example - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Graphics", "graphics-tab") - tab.combo_select("Type:", "Spice") - tab.combo_select("Listen type:", "None") - tab.find("OpenGL:", "check box").click() - tab.combo_check_default("graphics-rendernode", "0000") - self._finish(addhw, check=details) - - def testAddHosts(self): - """ - Add a few different USB and PCI devices - """ - details = self.app.open_details_window("test-clone-simple") - addhw = self._open_addhw_window(details) - - # Add USB device dup1 - tab = self._select_hw(addhw, "USB Host Device", "host-tab") - tab.find_fuzzy("HP Dup USB 1", "table cell").click() - self._finish(addhw, check=None) - self.app.click_alert_button("device is already in use by", "No") - self._finish(addhw, check=None) - self.app.click_alert_button("device is already in use by", "Yes") - lib.utils.check(lambda: details.active) - - # Add USB device dup2 - self._open_addhw_window(details) - tab = self._select_hw(addhw, "USB Host Device", "host-tab") - tab.find_fuzzy("HP Dup USB 2", "table cell").click() - self._finish(addhw, check=None) - self.app.click_alert_button("device is already in use by", "Yes") - lib.utils.check(lambda: details.active) - - # Add another USB device - self._open_addhw_window(details) - tab = self._select_hw(addhw, "USB Host Device", "host-tab") - tab.find_fuzzy("Cruzer Micro 256", "table cell").click() - self._finish(addhw, check=details) - - # Add PCI device - self._open_addhw_window(details) - tab = self._select_hw(addhw, "PCI Host Device", "host-tab") - tab.find_fuzzy("(Interface eth0)", "table cell").click() - self._finish(addhw, check=None) - self.app.click_alert_button("device is already in use by", "Yes") - lib.utils.check(lambda: details.active) - - - def testAddChars(self): - """ - Add a bunch of char devices - """ - details = self.app.open_details_window("test-clone-simple") - addhw = self._open_addhw_window(details) - - # Add console device - tab = self._select_hw(addhw, "Console", "char-tab") - tab.combo_select("Device Type:", "Pseudo TTY") - tab.combo_select("Type:", "Hypervisor default") - self._finish(addhw, check=details) - - # Add serial+file - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Serial", "char-tab") - tab.combo_select("Device Type:", "Output to a file") - tab.find("Path:", "text").set_text("/tmp/foo.log") - self._finish(addhw, check=details) - - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Parallel", "char-tab") - tab.combo_select("Device Type:", "UNIX") - self._finish(addhw, check=details) - - # Add spicevmc channel - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Channel", "char-tab") - tab.combo_check_default("char-target-name", ".*redhat.spice.0.*") - tab.combo_select("char-target-name", ".*webdav.*") - tab.combo_select("char-target-name", ".*org.qemu.guest_agent*") - self._finish(addhw, check=details) - - - def testAddLXCFilesystem(self): - """ - Adding LXC specific filesystems - """ - self.app.uri = tests.utils.URIs.lxc - - details = self.app.open_details_window("test-clone-simple") - addhw = self._open_addhw_window(details) - - # Add File+nbd share - tab = self._select_hw(addhw, "Filesystem", "filesystem-tab") - tab.combo_select("Type:", "File") - tab.combo_select("Driver:", "Nbd") - tab.combo_select("Format:", "qcow2") - - source = tab.find("Source path:", "text") - source.set_text("/foo/source") - tab.find("Browse...", "push button").click() - # Specific testing for dir vol handling for filesystem browse - browsewin = self.app.root.find("vmm-storage-browser") - browsewin.find_fuzzy("default-pool", "table cell").click() - browsewin.find_fuzzy("bochs-vol", "table cell").click() - choose = browsewin.find("Choose Volume") - lib.utils.check(lambda: not choose.sensitive) - browsewin.find_fuzzy("dir-vol", "table cell").click() - lib.utils.check(lambda: choose.sensitive) - choose.click() - lib.utils.check(lambda: addhw.active) - lib.utils.check( - lambda: source.text == "/dev/default-pool/dir-vol") - - tab.find_fuzzy("Export filesystem", "check").click() - # Use this to test some error.py logic for truncating large errors - badtarget = "a" * 1024 - tab.find("Target path:", "text").set_text(badtarget) - self._finish(addhw, check=None) - self.app.click_alert_button("aaa...", "Close") - tab.find("Target path:", "text").set_text("/foo/target") - self._finish(addhw, check=details) - - # Add RAM type - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Filesystem", "filesystem-tab") - tab.combo_select("Type:", "Ram") - tab.find("Usage:", "spin button").set_text("12345") - tab.find("Target path:", "text").set_text("/mem") - self._finish(addhw, check=details) - - - def testAddHWMisc1(self): - """ - Add some simple devices - """ - details = self.app.open_details_window("test-clone-simple") - addhw = self._open_addhw_window(details) - - # Add input - tab = self._select_hw(addhw, "Input", "input-tab") - tab.combo_select("Type:", "EvTouch") - self._finish(addhw, check=details) - - # Add sound - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Sound", "sound-tab") - tab.combo_select("Model:", "HDA") - self._finish(addhw, check=details) - - # Add video - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Video", "video-tab") - tab.combo_select("Model:", "Virtio") - self._finish(addhw, check=details) - - # Add watchdog - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Watchdog", "watchdog-tab") - tab.combo_select("Model:", "I6300") - tab.combo_select("Action:", "Pause the guest") - self._finish(addhw, check=details) - - # Add smartcard - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Smartcard", "smartcard-tab") - tab.combo_select("Mode:", "Passthrough") - self._finish(addhw, check=details) - - # Add TPM emulated - self._open_addhw_window(details) - tab = self._select_hw(addhw, "TPM", "tpm-tab") - self._finish(addhw, check=details) - - def testAddHWMisc2(self): - """ - Add some more simple devices" - """ - details = self.app.open_details_window("test-clone-simple") - addhw = self._open_addhw_window(details) - - # Add usb controller, to make usbredir work - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Controller", "controller-tab") - tab.combo_select("Type:", "USB") - self._finish(addhw, check=details) - - # Add usb redir - self._open_addhw_window(details) - tab = self._select_hw(addhw, "USB Redirection", "usbredir-tab") - tab.combo_select("Type:", "Spice") - self._finish(addhw, check=details) - - # Add basic filesystem - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Filesystem", "filesystem-tab") - tab.find("Source path:", "text").set_text("/foo/source") - tab.find("Target path:", "text").set_text("/foo/target") - self._finish(addhw, check=details) - - # Add TPM passthrough - self._open_addhw_window(details) - tab = self._select_hw(addhw, "TPM", "tpm-tab") - tab.combo_select("Model:", "TIS") - tab.combo_select("Backend:", "Passthrough") - tab.find("Device Path:", "text").set_text("/tmp/foo") - self._finish(addhw, check=details) - - # Add RNG - self._open_addhw_window(details) - tab = self._select_hw(addhw, "RNG", "rng-tab") - tab.find("Host Device:", "text").set_text("/dev/random") - self._finish(addhw, check=details) - - # Add Panic - self._open_addhw_window(details) - tab = self._select_hw(addhw, "Panic", "panic-tab") - tab.combo_select("Model:", "Hyper-V") - self._finish(addhw, check=details) - - # Add vsock - self._open_addhw_window(details) - tab = self._select_hw(addhw, "VirtIO VSOCK", "vsock-tab") - tab.find("vsock-auto").click() - tab.find("vsock-cid").set_text("7") - self._finish(addhw, check=details) - - def testAddHWUSBNone(self): - """ - Test some special case handling when VM has controller usb model='none' - """ - details = self.app.open_details_window("test alternate devs title", - shutdown=True) - addhw = self._open_addhw_window(details) - - # Add usb controller - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Controller", "controller-tab") - tab.combo_select("Type:", "USB") - self._finish(addhw, check=details) - - # Trigger a libvirt error to test error handling - addhw = self._open_addhw_window(details) - tab = self._select_hw(addhw, "Controller", "controller-tab") - combo = tab.find("Type:", "combo box") - combo.find(None, "text").set_text("foobar") - self._finish(addhw, check=None) - self.app.click_alert_button("Unable to add device", "Close") - lib.utils.check(lambda: addhw.active) - - def testAddHWCornerCases(self): - """ - Random addhardware related tests - """ - details = self.app.open_details_window("test-many-devices") - addhw = self._open_addhw_window(details) - - # Test cancel - addhw.find("Cancel", "push button").click() - - # Test live adding, error dialog, click no - self._open_addhw_window(details) - self._finish(addhw, check=None) - alert = self.app.root.find("vmm dialog", "alert") - alert.find( - "This device could not be attached to the running machine", - "label") - alert.find("Details", "toggle button").click_expander() - alert.find("No", "push button").click() - lib.utils.check(lambda: details.active) - - # Test live adding, error dialog, click yes - self._open_addhw_window(details) - self._finish(addhw, check=None) - alert = self.app.root.find("vmm dialog", "alert") - alert.find( - "This device could not be attached to the running machine", - "label") - alert.find("Details", "toggle button").click_expander() - alert.find("Yes", "push button").click() - lib.utils.check(lambda: alert.dead) - - def testAddHWXMLEdit(self): - """ - Test XML editor integration - """ - self.app.open(xmleditor_enabled=True) - details = self.app.open_details_window("test-clone-simple") - win = self._open_addhw_window(details) - - # Disk test, change path and make sure we error it is missing - win.find("XML", "page tab").click() - xmleditor = win.find("XML editor") - origpath = "/var/lib/libvirt/images/test-clone-simple.qcow2" - newpath = "/FOO/XMLEDIT/test1.img" - xmleditor.set_text(xmleditor.text.replace(origpath, newpath)) - self._finish(win, check=None) - self.app.click_alert_button("non-existent path", "Close") - - # Undo the bad change, change bus/target - xmleditor.set_text(xmleditor.text.replace(newpath, origpath)) - xmleditor.set_text(xmleditor.text.replace("hdb", "xvda")) - xmleditor.set_text(xmleditor.text.replace("ide", "xen")) - self._finish(win, check=details) - - # Verify the changes applied - details.find("Xen Disk 1").click() - lib.utils.check(lambda: details.active) - win = self._open_addhw_window(details) - tab = self._select_hw(win, "Storage", "storage-tab") - tab.find_fuzzy("Select or create", "radio").click() - tab.find("storage-browse", "push button").click() - browse = self.app.root.find("vmm-storage-browser") - browse.find(os.path.basename(origpath)) - browse.find("Cancel").click() - - # Select XML, switch to new dev type, verify we change focus - win.find("XML", "page tab").click() - xmleditor = win.find("XML editor") - lib.utils.check(lambda: xmleditor.showing) - tab = self._select_hw(win, "Network", "network-tab") - lib.utils.check(lambda: not xmleditor.showing) - - # Do standard xmleditor tests - finish = win.find("Finish", "push button") - lib.utils.test_xmleditor_interactions(self.app, win, finish) - win.find("Cancel", "push button").click() - lib.utils.check(lambda: not win.visible) + details = _open_app(app, "test-clone-simple") + addhw = _open_addhw(app, details) + + # Default SCSI + tab = _select_hw(addhw, "Controller", "controller-tab") + tab.combo_select("Type:", "SCSI") + _finish(addhw, check=details) + + # Virtio SCSI + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Controller", "controller-tab") + tab.combo_select("Type:", "SCSI") + tab.combo_select("Model:", "VirtIO SCSI") + _finish(addhw, check=details) + + # USB 2 + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Controller", "controller-tab") + tab.combo_select("Type:", "USB") + tab.combo_select("Model:", "USB 2") + _finish(addhw, check=details) + + # USB 3 + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Controller", "controller-tab") + tab.combo_select("Type:", "USB") + tab.combo_select("Model:", "USB 3") + # Can't add more than 1 USB controller, so finish isn't sensitive + finish = addhw.find("Finish", "push button") + lib.utils.check(lambda: not finish.sensitive) + + +def testAddCephDisk(app): + """ + Add a disk with a ceph volume, ensure it maps correctly + """ + details = _open_app(app, "test-clone-simple") + addhw = _open_addhw(app, details) + + # Select ceph volume for disk + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + tab.find("storage-browse", "push button").click() + browse = app.root.find("vmm-storage-browser") + browse.find_fuzzy("rbd-ceph", "table cell").bring_on_screen().click() + browse.find_fuzzy("some-rbd-vol", "table cell").click() + browse.find("Choose Volume", "push button").click() + _finish(addhw, check=details) + + # Check disk details, make sure it correctly selected volume + details.find("IDE Disk 2", "table cell").click() + tab = details.find("disk-tab") + lib.utils.check(lambda: tab.showing) + disk_path = tab.find("disk-source-path") + lib.utils.check(lambda: "rbd://" in disk_path.text) + + +def testAddDisks(app): + """ + Add various disk configs and test storage browser + """ + details = _open_app(app, "test-clone-simple") + addhw = _open_addhw(app, details) + + # Default disk + tab = _select_hw(addhw, "Storage", "storage-tab") + _finish(addhw, check=details) + + # Disk with some tweaks + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.combo_select("Bus type:", "VirtIO") + tab.find("Advanced options", "toggle button").click_expander() + tab.find("Shareable:", "check box").click() + tab.find("Readonly:", "check box").click() + tab.find("Serial:", "text").set_text("ZZZZ") + tab.combo_select("Cache mode:", "none") + tab.combo_select("Discard mode:", "ignore") + tab.combo_select("Detect zeroes:", "unmap") + # Size too big + tab.find("GiB", "spin button").set_text("2000") + _finish(addhw, check=None) + app.click_alert_button("not enough free space", "Close") + tab.find("GiB", "spin button").set_text("1.5") + _finish(addhw, check=details) + + # USB disk with removable setting + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.combo_select("Bus type:", "USB") + tab.find("Advanced options", "toggle button").click_expander() + tab.find("Removable:", "check box").click() + _finish(addhw, check=details) + + # Managed storage tests + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + _finish(addhw, check=None) + app.click_alert_button("storage path must be specified", "OK") + tab.find("storage-browse", "push button").click() + browse = app.root.find("vmm-storage-browser") + + # Create a vol, refresh, then delete it + browse.find_fuzzy("default-pool", "table cell").click() + browse.find("vol-new", "push button").click() + newvol = app.find_window("Add a Storage Volume") + newname = "a-newvol" + newvol.find("Name:", "text").set_text(newname) + newvol.find("Finish", "push button").click() + lib.utils.check(lambda: not newvol.showing) + volcell = browse.find(newname, "table cell") + lib.utils.check(lambda: volcell.selected) + browse.find("vol-refresh", "push button").click() + volcell = browse.find(newname, "table cell") + lib.utils.check(lambda: volcell.selected) + browse.find("vol-delete", "push button").click() + app.click_alert_button("permanently delete the volume", "Yes") + lib.utils.check(lambda: volcell.dead) + + # Test browse local + browse.find("Browse Local", "push button").click() + chooser = app.root.find( + "Locate existing storage", "file chooser") + + # use filename that is near the beginning of the file list when sorted, + # as the row in the file dialog may become scrolled out of the view and + # cause the test to fail + fname = "COPYING" + chooser.find(fname, "table cell").click() + chooser.find("Open", "push button").click() + lib.utils.check(lambda: not chooser.showing) + lib.utils.check(lambda: addhw.active) + storageent = tab.find("storage-entry") + lib.utils.check(lambda: ("/" + fname) in storageent.text) + + # Reopen dialog, select a volume, etic + tab.find("storage-browse", "push button").click() + browse = app.root.find("vmm-storage-browser") + + browse.find_fuzzy("disk-pool", "table cell").click() + browse.find("diskvol1", "table cell").click() + browse.find("Choose Volume", "push button").click() + lib.utils.check(lambda: "/diskvol1" in storageent.text) + _finish(addhw, check=None) + app.click_alert_button("already in use by", "No") + _finish(addhw, check=None) + app.click_alert_button("already in use by", "Yes") + lib.utils.check(lambda: details.active) + + + # choose file for floppy + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.combo_select("Device type:", "Floppy device") + diskradio = tab.find_fuzzy("Create a disk image", "radio") + lib.utils.check(lambda: not diskradio.sensitive) + tab.find("storage-entry").set_text("/dev/default-pool/bochs-vol") + _finish(addhw, check=details) + + # empty cdrom + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.combo_select("Device type:", "CDROM device") + tab.combo_select("Bus type:", "SCSI") + _finish(addhw, check=details) + + +@_search_permissions_decorator +def testAddDiskSearchPermsCheckbox(app, uri, tmpdir): + """ + Test search permissions 'no' and checkbox case + """ + app.uri = uri + details = _open_app(app, "test-clone-simple") + + # Say 'No' but path should still work due to test driver + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo1.img" + tab.find("storage-entry").set_text(path) + _finish(addhw, check=None) + app.click_alert_button("emulator may not have", "No") + lib.utils.check(lambda: details.active) + + # Say 'don't ask again' + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo2.img" + tab.find("storage-entry").set_text(path) + _finish(addhw, check=None) + alert = app.root.find_fuzzy("vmm dialog", "alert") + alert.find_fuzzy("Don't ask", "check box").click() + app.click_alert_button("emulator may not have", "No") + lib.utils.check(lambda: details.active) + + # Confirm it doesn't ask about path again + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo3.img" + tab.find("storage-entry").set_text(path) + _finish(addhw, check=details) + + +@_search_permissions_decorator +def testAddDiskSearchPermsSuccess(app, uri, tmpdir): + """ + Select 'Yes' for search perms fixing + """ + app.uri = uri + details = _open_app(app, "test-clone-simple") + + # Say 'Yes' + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo1.img" + tab.find("storage-entry").set_text(path) + _finish(addhw, check=None) + app.click_alert_button("emulator may not have", "Yes") + lib.utils.check(lambda: details.active) + + # Confirm it doesn't ask about path again + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo3.img" + tab.find("storage-entry").set_text(path) + _finish(addhw, check=details) + + +@_search_permissions_decorator +def testAddDiskSearchPermsFail(app, uri, tmpdir): + """ + Force perms fixing to fail + """ + app.uri = uri + details = _open_app(app, "test-clone-simple", + break_setfacl=True) + + # Say 'Yes' and it should fail, then blacklist the paths + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo1.img" + tab.find("storage-entry").set_text(path) + _finish(addhw, check=None) + app.click_alert_button("emulator may not have", "Yes") + alert = app.root.find("vmm dialog", "alert") + alert.find_fuzzy("Errors were encountered", "label") + alert.find_fuzzy("Don't ask", "check box").click() + alert.find_fuzzy("OK", "push button").click() + lib.utils.check(lambda: details.active) + + # Confirm it doesn't ask about path again + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + path = tmpdir + "/foo2.img" + tab.find("storage-entry").set_text(path) + _finish(addhw, check=details) + + +def testAddNetworks(app): + """ + Test various network configs + """ + details = _open_app(app, "test-clone-simple") + addhw = _open_addhw(app, details) + + # Basic network + opts + tab = _select_hw(addhw, "Network", "network-tab") + tab.combo_select("net-source", "Virtual network 'default'") + tab.find("MAC Address Field", "text").set_text("00:11:00:11:00:11") + tab.combo_select("Device model:", "virtio") + _finish(addhw, check=details) + + # Manual macvtap + _open_addhw(app, details) + tab = _select_hw(addhw, "Network", "network-tab") + tab.combo_select("net-source", "Macvtap device...") + tab.find("Device name:", "text").set_text("macvtapfoo7") + _finish(addhw, check=details) + + # Manual bridge. Also trigger MAC collision + _open_addhw(app, details) + tab = _select_hw(addhw, "Network", "network-tab") + tab.find("mac-address-enable", "check box").click() + tab.combo_select("net-source", "Bridge device...") + tab.find("Device name:", "text").set_text("zbr0") + _finish(addhw, check=None) + # Check MAC validation error + app.click_alert_button("00:11:22:33:44:55", "Close") + + # Fix MAC + tab.find("mac-address-enable", "check box").click() + tab.find("MAC Address Field", "text").set_text("00:11:0A:11:00:11") + _finish(addhw, check=details) + + + +def testAddGraphics(app): + """ + Graphics device testing + """ + details = _open_app(app, "test-clone-simple") + addhw = _open_addhw(app, details) + + # VNC example + tab = _select_hw(addhw, "Graphics", "graphics-tab") + tab.combo_select("Type:", "VNC") + tab.combo_select("Listen type:", "Address") + tab.combo_select("Address:", "All interfaces") + tab.find("graphics-port-auto", "check").click() + tab.find("graphics-port", "spin button").set_text("1234") + tab.find("Password:", "check").click() + passwd = tab.find_fuzzy("graphics-password", "text") + newpass = "foobar" + passwd.typeText(newpass) + tab.find("Show password", "check").click() + lib.utils.check(lambda: passwd.text == newpass) + tab.find("Show password", "check").click() + lib.utils.check(lambda: passwd.text != newpass) + _finish(addhw, check=None) + # Catch a port error + app.click_alert_button("Port must be above 5900", "Close") + tab.find("graphics-port", "spin button").set_text("5920") + _finish(addhw, check=details) + + # Spice regular example + _open_addhw(app, details) + tab = _select_hw(addhw, "Graphics", "graphics-tab") + tab.combo_select("Type:", "Spice") + _finish(addhw, check=details) + + # Spice GL example + _open_addhw(app, details) + tab = _select_hw(addhw, "Graphics", "graphics-tab") + tab.combo_select("Type:", "Spice") + tab.combo_select("Listen type:", "None") + tab.find("OpenGL:", "check box").click() + tab.combo_check_default("graphics-rendernode", "0000") + _finish(addhw, check=details) + + +def testAddHosts(app): + """ + Add a few different USB and PCI devices + """ + details = _open_app(app, "test-clone-simple") + addhw = _open_addhw(app, details) + + # Add USB device dup1 + tab = _select_hw(addhw, "USB Host Device", "host-tab") + tab.find_fuzzy("HP Dup USB 1", "table cell").click() + _finish(addhw, check=None) + app.click_alert_button("device is already in use by", "No") + _finish(addhw, check=None) + app.click_alert_button("device is already in use by", "Yes") + lib.utils.check(lambda: details.active) + + # Add USB device dup2 + _open_addhw(app, details) + tab = _select_hw(addhw, "USB Host Device", "host-tab") + tab.find_fuzzy("HP Dup USB 2", "table cell").click() + _finish(addhw, check=None) + app.click_alert_button("device is already in use by", "Yes") + lib.utils.check(lambda: details.active) + + # Add another USB device + _open_addhw(app, details) + tab = _select_hw(addhw, "USB Host Device", "host-tab") + tab.find_fuzzy("Cruzer Micro 256", "table cell").click() + _finish(addhw, check=details) + + # Add PCI device + _open_addhw(app, details) + tab = _select_hw(addhw, "PCI Host Device", "host-tab") + tab.find_fuzzy("(Interface eth0)", "table cell").click() + _finish(addhw, check=None) + app.click_alert_button("device is already in use by", "Yes") + lib.utils.check(lambda: details.active) + + + +def testAddChars(app): + """ + Add a bunch of char devices + """ + details = _open_app(app, "test-clone-simple") + addhw = _open_addhw(app, details) + + # Add console device + tab = _select_hw(addhw, "Console", "char-tab") + tab.combo_select("Device Type:", "Pseudo TTY") + tab.combo_select("Type:", "Hypervisor default") + _finish(addhw, check=details) + + # Add serial+file + _open_addhw(app, details) + tab = _select_hw(addhw, "Serial", "char-tab") + tab.combo_select("Device Type:", "Output to a file") + tab.find("Path:", "text").set_text("/tmp/foo.log") + _finish(addhw, check=details) + + _open_addhw(app, details) + tab = _select_hw(addhw, "Parallel", "char-tab") + tab.combo_select("Device Type:", "UNIX") + _finish(addhw, check=details) + + # Add spicevmc channel + _open_addhw(app, details) + tab = _select_hw(addhw, "Channel", "char-tab") + tab.combo_check_default("char-target-name", ".*redhat.spice.0.*") + tab.combo_select("char-target-name", ".*webdav.*") + tab.combo_select("char-target-name", ".*org.qemu.guest_agent*") + _finish(addhw, check=details) + + + +def testAddLXCFilesystem(app): + """ + Adding LXC specific filesystems + """ + app.uri = tests.utils.URIs.lxc + + details = _open_app(app, "test-clone-simple") + addhw = _open_addhw(app, details) + + # Add File+nbd share + tab = _select_hw(addhw, "Filesystem", "filesystem-tab") + tab.combo_select("Type:", "File") + tab.combo_select("Driver:", "Nbd") + tab.combo_select("Format:", "qcow2") + + source = tab.find("Source path:", "text") + source.set_text("/foo/source") + tab.find("Browse...", "push button").click() + # Specific testing for dir vol handling for filesystem browse + browsewin = app.root.find("vmm-storage-browser") + browsewin.find_fuzzy("default-pool", "table cell").click() + browsewin.find_fuzzy("bochs-vol", "table cell").click() + choose = browsewin.find("Choose Volume") + lib.utils.check(lambda: not choose.sensitive) + browsewin.find_fuzzy("dir-vol", "table cell").click() + lib.utils.check(lambda: choose.sensitive) + choose.click() + lib.utils.check(lambda: addhw.active) + lib.utils.check( + lambda: source.text == "/dev/default-pool/dir-vol") + + tab.find_fuzzy("Export filesystem", "check").click() + # Use this to test some error.py logic for truncating large errors + badtarget = "a" * 1024 + tab.find("Target path:", "text").set_text(badtarget) + _finish(addhw, check=None) + app.click_alert_button("aaa...", "Close") + tab.find("Target path:", "text").set_text("/foo/target") + _finish(addhw, check=details) + + # Add RAM type + _open_addhw(app, details) + tab = _select_hw(addhw, "Filesystem", "filesystem-tab") + tab.combo_select("Type:", "Ram") + tab.find("Usage:", "spin button").set_text("12345") + tab.find("Target path:", "text").set_text("/mem") + _finish(addhw, check=details) + + + +def testAddHWMisc1(app): + """ + Add some simple devices + """ + details = _open_app(app, "test-clone-simple") + addhw = _open_addhw(app, details) + + # Add input + tab = _select_hw(addhw, "Input", "input-tab") + tab.combo_select("Type:", "EvTouch") + _finish(addhw, check=details) + + # Add sound + _open_addhw(app, details) + tab = _select_hw(addhw, "Sound", "sound-tab") + tab.combo_select("Model:", "HDA") + _finish(addhw, check=details) + + # Add video + _open_addhw(app, details) + tab = _select_hw(addhw, "Video", "video-tab") + tab.combo_select("Model:", "Virtio") + _finish(addhw, check=details) + + # Add watchdog + _open_addhw(app, details) + tab = _select_hw(addhw, "Watchdog", "watchdog-tab") + tab.combo_select("Model:", "I6300") + tab.combo_select("Action:", "Pause the guest") + _finish(addhw, check=details) + + # Add smartcard + _open_addhw(app, details) + tab = _select_hw(addhw, "Smartcard", "smartcard-tab") + tab.combo_select("Mode:", "Passthrough") + _finish(addhw, check=details) + + # Add TPM emulated + _open_addhw(app, details) + tab = _select_hw(addhw, "TPM", "tpm-tab") + _finish(addhw, check=details) + + +def testAddHWMisc2(app): + """ + Add some more simple devices" + """ + details = _open_app(app, "test-clone-simple") + addhw = _open_addhw(app, details) + + # Add usb controller, to make usbredir work + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Controller", "controller-tab") + tab.combo_select("Type:", "USB") + _finish(addhw, check=details) + + # Add usb redir + _open_addhw(app, details) + tab = _select_hw(addhw, "USB Redirection", "usbredir-tab") + tab.combo_select("Type:", "Spice") + _finish(addhw, check=details) + + # Add basic filesystem + _open_addhw(app, details) + tab = _select_hw(addhw, "Filesystem", "filesystem-tab") + tab.find("Source path:", "text").set_text("/foo/source") + tab.find("Target path:", "text").set_text("/foo/target") + _finish(addhw, check=details) + + # Add TPM passthrough + _open_addhw(app, details) + tab = _select_hw(addhw, "TPM", "tpm-tab") + tab.combo_select("Model:", "TIS") + tab.combo_select("Backend:", "Passthrough") + tab.find("Device Path:", "text").set_text("/tmp/foo") + _finish(addhw, check=details) + + # Add RNG + _open_addhw(app, details) + tab = _select_hw(addhw, "RNG", "rng-tab") + tab.find("Host Device:", "text").set_text("/dev/random") + _finish(addhw, check=details) + + # Add Panic + _open_addhw(app, details) + tab = _select_hw(addhw, "Panic", "panic-tab") + tab.combo_select("Model:", "Hyper-V") + _finish(addhw, check=details) + + # Add vsock + _open_addhw(app, details) + tab = _select_hw(addhw, "VirtIO VSOCK", "vsock-tab") + tab.find("vsock-auto").click() + tab.find("vsock-cid").set_text("7") + _finish(addhw, check=details) + + +def testAddHWUSBNone(app): + """ + Test some special case handling when VM has controller usb model='none' + """ + details = _open_app(app, "test-alternate-devs", + title="test alternate devs title", + shutdown=True) + addhw = _open_addhw(app, details) + + # Add usb controller + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Controller", "controller-tab") + tab.combo_select("Type:", "USB") + _finish(addhw, check=details) + + # Trigger a libvirt error to test error handling + addhw = _open_addhw(app, details) + tab = _select_hw(addhw, "Controller", "controller-tab") + combo = tab.find("Type:", "combo box") + combo.find(None, "text").set_text("foobar") + _finish(addhw, check=None) + app.click_alert_button("Unable to add device", "Close") + lib.utils.check(lambda: addhw.active) + + +def testAddHWCornerCases(app): + """ + Random addhardware related tests + """ + details = _open_app(app, "test-many-devices") + addhw = _open_addhw(app, details) + + # Test cancel + addhw.find("Cancel", "push button").click() + + # Test live adding, error dialog, click no + _open_addhw(app, details) + _finish(addhw, check=None) + alert = app.root.find("vmm dialog", "alert") + alert.find( + "This device could not be attached to the running machine", + "label") + alert.find("Details", "toggle button").click_expander() + alert.find("No", "push button").click() + lib.utils.check(lambda: details.active) + + # Test live adding, error dialog, click yes + _open_addhw(app, details) + _finish(addhw, check=None) + alert = app.root.find("vmm dialog", "alert") + alert.find( + "This device could not be attached to the running machine", + "label") + alert.find("Details", "toggle button").click_expander() + alert.find("Yes", "push button").click() + lib.utils.check(lambda: alert.dead) + + +def testAddHWXMLEdit(app): + """ + Test XML editor integration + """ + details = _open_app(app, "test-clone-simple", + xmleditor_enabled=True) + win = _open_addhw(app, details) + + # Disk test, change path and make sure we error it is missing + win.find("XML", "page tab").click() + xmleditor = win.find("XML editor") + origpath = "/var/lib/libvirt/images/test-clone-simple.qcow2" + newpath = "/FOO/XMLEDIT/test1.img" + xmleditor.set_text(xmleditor.text.replace(origpath, newpath)) + _finish(win, check=None) + app.click_alert_button("non-existent path", "Close") + + # Undo the bad change, change bus/target + xmleditor.set_text(xmleditor.text.replace(newpath, origpath)) + xmleditor.set_text(xmleditor.text.replace("hdb", "xvda")) + xmleditor.set_text(xmleditor.text.replace("ide", "xen")) + _finish(win, check=details) + + # Verify the changes applied + details.find("Xen Disk 1").click() + lib.utils.check(lambda: details.active) + win = _open_addhw(app, details) + tab = _select_hw(win, "Storage", "storage-tab") + tab.find_fuzzy("Select or create", "radio").click() + tab.find("storage-browse", "push button").click() + browse = app.root.find("vmm-storage-browser") + browse.find(os.path.basename(origpath)) + browse.find("Cancel").click() + + # Select XML, switch to new dev type, verify we change focus + win.find("XML", "page tab").click() + xmleditor = win.find("XML editor") + lib.utils.check(lambda: xmleditor.showing) + tab = _select_hw(win, "Network", "network-tab") + lib.utils.check(lambda: not xmleditor.showing) + + # Do standard xmleditor tests + finish = win.find("Finish", "push button") + lib.utils.test_xmleditor_interactions(app, win, finish) + win.find("Cancel", "push button").click() + lib.utils.check(lambda: not win.visible) diff --git a/tests/uitests/test_cli.py b/tests/uitests/test_cli.py index d33defe6..93ab1721 100644 --- a/tests/uitests/test_cli.py +++ b/tests/uitests/test_cli.py @@ -6,192 +6,201 @@ import unittest.mock from . import lib -class VMMCLI(lib.testcase.UITestCase): +# UI tests for virt-manager's command line --show options + + +def testShowNewVM(app): + app.open( + uri="test:///default", + extra_opts=["--show-domain-creator"]) + lib.utils.check(lambda: app.topwin.name == "New VM") + app.topwin.keyCombo("F4") + app.wait_for_exit() + + +def testShowHost(app): + app.open( + uri="test:///default", + extra_opts=["--show-host-summary"]) + + lib.utils.check(lambda: app.topwin.name == "test default - Connection Details") + nametext = app.topwin.find_fuzzy("Name:", "text") + lib.utils.check(lambda: nametext.text == "test default") + app.topwin.keyCombo("F4") + app.wait_for_exit() + + +def testShowDetails(app): + app.open( + extra_opts=["--show-domain-editor", "test-clone-simple"]) + + lib.utils.check(lambda: "test-clone-simple on" in app.topwin.name) + rlabel = app.topwin.find_fuzzy("Guest is not running", "label") + lib.utils.check(lambda: not rlabel.showing) + addhw = app.topwin.find_fuzzy("add-hardware", "button") + lib.utils.check(lambda: addhw.showing) + app.topwin.keyCombo("F4") + app.wait_for_exit() + + +def testShowPerformance(app): + domid = "1" + app.open( + uri="test:///default", + extra_opts=["--show-domain-performance", domid]) + + lib.utils.check(lambda: "test on" in app.topwin.name) + cpulabel = app.topwin.find_fuzzy("CPU usage", "label") + lib.utils.check(lambda: cpulabel.showing) + + +def testShowConsole(app): + # UUID of test-clone-simple + uuid = "12345678-1234-ffff-1234-12345678ffff" + app.open( + extra_opts=["--show-domain-console", uuid]) + + lib.utils.check(lambda: "test-clone-simple on" in app.topwin.name) + rlabel = app.topwin.find_fuzzy("Guest is not running", "label") + lib.utils.check(lambda: rlabel.showing) + addhw = app.topwin.find_fuzzy("add-hardware", "button") + lib.utils.check(lambda: not addhw.showing) + + +def testShowDelete(app): + app.open( + uri="test:///default", + extra_opts=["--show-domain-delete", "test"], + window_name="Delete") + # Ensure details opened too + app.root.find("test on", "frame", check_active=False) + + delete = app.topwin + delete.find_fuzzy("Delete", "button").click() + app.wait_for_exit() + + + +def testShowRemoteDBusConnect(app): """ - UI tests for virt-manager's command line --show options + Test the remote app dbus connection """ + app.open() + lib.utils.check(lambda: "testdriver" in app.topwin.fmt_nodes()) + lib.utils.check(lambda: "test default" not in app.topwin.fmt_nodes()) - ############## - # Test cases # - ############## + def _run_remote(opts): + newapp = lib.app.VMMDogtailApp("test:///default") + newapp.open(check_already_running=False, + extra_opts=opts) + lib.utils.check(lambda: not newapp.is_running()) + vapps = [a for a in newapp.tree.root.applications() if + a.name == "virt-manager"] + lib.utils.check(lambda: len(vapps) == 1) + # Ensure connection showed up + app.topwin.find("test default", "table cell") - def testShowNewVM(self): - self.app.open( - uri="test:///default", - extra_opts=["--show-domain-creator"]) - lib.utils.check(lambda: self.app.topwin.name == "New VM") - self.app.topwin.keyCombo("F4") - self.app.wait_for_exit() - - def testShowHost(self): - self.app.open( - uri="test:///default", - extra_opts=["--show-host-summary"]) - - lib.utils.check(lambda: self.app.topwin.name == "test default - Connection Details") - nametext = self.app.topwin.find_fuzzy("Name:", "text") - lib.utils.check(lambda: nametext.text == "test default") - self.app.topwin.keyCombo("F4") - self.app.wait_for_exit() - - def testShowDetails(self): - self.app.open( - extra_opts=["--show-domain-editor", "test-clone-simple"]) - - lib.utils.check(lambda: "test-clone-simple on" in self.app.topwin.name) - rlabel = self.app.topwin.find_fuzzy("Guest is not running", "label") - lib.utils.check(lambda: not rlabel.showing) - addhw = self.app.topwin.find_fuzzy("add-hardware", "button") - lib.utils.check(lambda: addhw.showing) - self.app.topwin.keyCombo("F4") - self.app.wait_for_exit() - - def testShowPerformance(self): - domid = "1" - self.app.open( - uri="test:///default", - extra_opts=["--show-domain-performance", domid]) - - lib.utils.check(lambda: "test on" in self.app.topwin.name) - cpulabel = self.app.topwin.find_fuzzy("CPU usage", "label") - lib.utils.check(lambda: cpulabel.showing) - - def testShowConsole(self): - # UUID of test-clone-simple - uuid = "12345678-1234-ffff-1234-12345678ffff" - self.app.open( - extra_opts=["--show-domain-console", uuid]) - - lib.utils.check(lambda: "test-clone-simple on" in self.app.topwin.name) - rlabel = self.app.topwin.find_fuzzy("Guest is not running", "label") - lib.utils.check(lambda: rlabel.showing) - addhw = self.app.topwin.find_fuzzy("add-hardware", "button") - lib.utils.check(lambda: not addhw.showing) - - def testShowDelete(self): - self.app.open( - uri="test:///default", - extra_opts=["--show-domain-delete", "test"], - window_name="Delete") - # Ensure details opened too - self.app.root.find("test on", "frame", - check_active=False) - - delete = self.app.topwin - delete.find_fuzzy("Delete", "button").click() - self.app.wait_for_exit() + _run_remote([]) + # Run remote again to trigger engine.py code when a connection + # is already there and connected + _run_remote(["--show-domain-console=test"]) - def testShowRemoteDBusConnect(self): - """ - Test the remote app dbus connection - """ - self.app.open() - lib.utils.check(lambda: "testdriver" in self.app.topwin.fmt_nodes()) - lib.utils.check(lambda: "test default" not in self.app.topwin.fmt_nodes()) +def testShowCLIError(app): + # Unknown option + app.open( + extra_opts=["--idontexist"]) + app.click_alert_button("Unhandled command line", "Close") + lib.utils.check(lambda: not app.is_running()) - def _run_remote(opts): - newapp = lib.app.VMMDogtailApp("test:///default") - newapp.open(check_already_running=False, - extra_opts=opts) - lib.utils.check(lambda: not newapp.is_running()) - vapps = [a for a in newapp.tree.root.applications() if - a.name == "virt-manager"] - lib.utils.check(lambda: len(vapps) == 1) - # Ensure connection showed up - self.app.topwin.find("test default", "table cell") + # Missing VM + app.open( + uri="test:///default", + extra_opts=["--show-domain-delete", "IDONTEXIST"]) + app.click_alert_button("does not have VM", "Close") + lib.utils.check(lambda: not app.is_running()) - _run_remote([]) - # Run remote again to trigger engine.py code when a connection - # is already there and connected - _run_remote(["--show-domain-console=test"]) + # Bad URI + baduri = "fribfrobfroo" + app = lib.app.VMMDogtailApp(baduri) + app.click_alert_button(baduri, "Close") + lib.utils.check(lambda: not app.is_running()) - def testShowCLIError(self): - # Unknown option - self.app.open( - extra_opts=["--idontexist"]) - self.app.click_alert_button("Unhandled command line", "Close") - lib.utils.check(lambda: not self.app.is_running()) - # Missing VM - self.app.open( - uri="test:///default", - extra_opts=["--show-domain-delete", "IDONTEXIST"]) - self.app.click_alert_button("does not have VM", "Close") - lib.utils.check(lambda: not self.app.is_running()) +def testCLIFirstRunURIGood(app): + # Emulate first run with a URI that will succeed + app.open(use_uri=False, firstrun_uri="test:///default") + app.sleep(1) + app.root.find("test default", "table cell") - # Bad URI - baduri = "fribfrobfroo" - self.app = lib.app.VMMDogtailApp(baduri) - self.app.click_alert_button(baduri, "Close") - lib.utils.check(lambda: not self.app.is_running()) - def testCLIFirstRunURIGood(self): - # Emulate first run with a URI that will succeed - self.app.open(use_uri=False, firstrun_uri="test:///default") - self.app.sleep(1) - self.app.root.find("test default", "table cell") +def testCLIFirstRunURIBad(app): + # Emulate first run with a URI that will not succeed + app.open(use_uri=False, firstrun_uri="bad:///uri") + app.sleep(1) + app.topwin.find("bad uri", "table cell") + app.click_alert_button("bad:///uri", "Close") - def testCLIFirstRunURIBad(self): - # Emulate first run with a URI that will not succeed - self.app.open(use_uri=False, firstrun_uri="bad:///uri") - self.app.sleep(1) - self.app.topwin.find("bad uri", "table cell") - self.app.click_alert_button("bad:///uri", "Close") - def testCLIFirstRunNoLibvirtd(self): - # Emulate first run with no libvirtd detected - self.app.open(use_uri=False, firstrun_uri="bad:///uri", - extra_opts=["--test-options=fake-no-libvirtd"]) - errlabel = self.app.topwin.find("error-label") - lib.utils.check( - lambda: "Checking for virtualization" in errlabel.text) - lib.utils.check( - lambda: "libvirtd service does not appear" in errlabel.text) - lib.utils.check( - lambda: "detect a default hypervisor" in errlabel.text) +def testCLIFirstRunNoLibvirtd(app): + # Emulate first run with no libvirtd detected + app.open(use_uri=False, firstrun_uri="bad:///uri", + extra_opts=["--test-options=fake-no-libvirtd"]) + errlabel = app.topwin.find("error-label") + lib.utils.check( + lambda: "Checking for virtualization" in errlabel.text) + lib.utils.check( + lambda: "libvirtd service does not appear" in errlabel.text) + lib.utils.check( + lambda: "detect a default hypervisor" in errlabel.text) - def testCLITraceLibvirt(self): - # Just test this for code coverage - self.app.open(keyfile="allstats.ini", - extra_opts=["--trace-libvirt=mainloop"]) - # Give it a little time to work - self.app.sleep(2) - lib.utils.check(lambda: self.app.topwin.active) - def testCLILeakDebug(self): - # Just test this for code coverage - self.app.open(keyfile="allstats.ini", - extra_opts=["--test-options=leak-debug"]) - self.app.sleep(2) - # Give it a little time to work - lib.utils.check(lambda: self.app.topwin.active) - self.app.topwin.keyCombo("F4") - self.app.wait_for_exit() +def testCLITraceLibvirt(app): + # Just test this for code coverage + app.open(keyfile="allstats.ini", + extra_opts=["--trace-libvirt=mainloop"]) + # Give it a little time to work + app.sleep(2) + lib.utils.check(lambda: app.topwin.active) - def testCLINoFirstRun(self): - # Test a simple case of loading without any config override - self.app.open(first_run=False, enable_libguestfs=None, use_uri=False) - self.app.sleep(2) - lib.utils.check(lambda: self.app.topwin.showing) - def testCLINoFork(self): - # Test app without forking - self.app.open(first_run=False, enable_libguestfs=None, - use_uri=False, no_fork=False) - self.app.wait_for_exit() - lib.utils.check(lambda: self.app.topwin.showing) - self.app.topwin.keyCombo("F4") - # Wait for app to exit, we don't have any other way - self.app.sleep(2) +def testCLILeakDebug(app): + # Just test this for code coverage + app.open(keyfile="allstats.ini", + extra_opts=["--test-options=leak-debug"]) + app.sleep(2) + # Give it a little time to work + lib.utils.check(lambda: app.topwin.active) + app.topwin.keyCombo("F4") + app.wait_for_exit() - def testCLIGTKArgs(self): - # Ensure gtk arg passthrough works - self.app.open(extra_opts=["--gtk-debug=misc"]) - lib.utils.check(lambda: self.app.topwin.showing) - @unittest.mock.patch.dict('os.environ', {"DISPLAY": ""}) - def testCLINoDisplay(self): - # Ensure missing display exits - self.app.open(will_fail=True) - self.app.wait_for_exit() +def testCLINoFirstRun(app): + # Test a simple case of loading without any config override + app.open(first_run=False, enable_libguestfs=None, use_uri=False) + app.sleep(2) + lib.utils.check(lambda: app.topwin.showing) + + +def testCLINoFork(app): + # Test app without forking + app.open(first_run=False, enable_libguestfs=None, + use_uri=False, no_fork=False) + app.wait_for_exit() + lib.utils.check(lambda: app.topwin.showing) + app.topwin.keyCombo("F4") + # Wait for app to exit, we don't have any other way + app.sleep(2) + + +def testCLIGTKArgs(app): + # Ensure gtk arg passthrough works + app.open(extra_opts=["--gtk-debug=misc"]) + lib.utils.check(lambda: app.topwin.showing) + + +@unittest.mock.patch.dict('os.environ', {"DISPLAY": ""}) +def testCLINoDisplay(app): + # Ensure missing display exits + app.open(will_fail=True) + app.wait_for_exit() diff --git a/tests/uitests/test_clonevm.py b/tests/uitests/test_clonevm.py index 82f281e1..2e44dac9 100644 --- a/tests/uitests/test_clonevm.py +++ b/tests/uitests/test_clonevm.py @@ -27,240 +27,225 @@ class _CloneRow: self.txtcell.click() -class CloneVM(lib.testcase.UITestCase): - """ - UI tests for virt-manager's CloneVM wizard - """ +def _get_all_rows(win): + slist = win.find("storage-list") + def pred(node): + return node.roleName == "table cell" + cells = slist.findChildren(pred, isLambda=True) - ################### - # Private helpers # - ################### - - def _open_window(self, vmname): - # Launch wizard via right click menu - manager = self.app.topwin - manager.click_title() - lib.utils.check(lambda: manager.active) - c = manager.find_fuzzy(vmname, "table cell") - self.app.sleep(.3) - c.click() - c.click(button=3) - item = self.app.root.find("Clone...", "menu item") - item.point() - self.app.sleep(.5) - item.click() - return self.app.root.find("Clone Virtual Machine", "frame") - - def _get_all_rows(self, win): - slist = win.find("storage-list") - def pred(node): - return node.roleName == "table cell" - cells = slist.findChildren(pred, isLambda=True) - - idx = 0 - rows = [] - cellcount = 6 - while idx < len(cells): - rows.append(_CloneRow(*cells[idx:idx + cellcount])) - idx += cellcount - # Skip the next row which is always a separator - idx += cellcount - return rows + idx = 0 + rows = [] + cellcount = 6 + while idx < len(cells): + rows.append(_CloneRow(*cells[idx:idx + cellcount])) + idx += cellcount + # Skip the next row which is always a separator + idx += cellcount + return rows - ############## - # Test cases # - ############## +############################################## +# UI tests for virt-manager's CloneVM wizard # +############################################## - def testCloneSimple(self): - # Disable predictable so UUID generation doesn't collide - uri = tests.utils.URIs.test_full.replace(",predictable", "") - self.app.uri = uri +def testCloneSimple(app): + # Disable predictable so UUID generation doesn't collide + uri = tests.utils.URIs.test_full.replace(",predictable", "") + app.uri = uri - # Clone 'test-clone-simple' which is the most basic case - # Cancel, and reopen - win = self._open_window("test-clone-simple") - win.find("Cancel", "push button").click() - lib.utils.check(lambda: not win.showing) + # Clone 'test-clone-simple' which is the most basic case + # Cancel, and reopen + win = app.manager_open_clone("test-clone-simple") + win.find("Cancel", "push button").click() + lib.utils.check(lambda: not win.showing) - # Do default clone - win = self._open_window("test-clone-simple") - rows = self._get_all_rows(win) - assert len(rows) == 1 - assert rows[0].is_clone_requested - rows[0].check_in_text("test-clone-simple.img") + # Do default clone + win = app.manager_open_clone("test-clone-simple") + rows = _get_all_rows(win) + assert len(rows) == 1 + assert rows[0].is_clone_requested + rows[0].check_in_text("test-clone-simple.img") - win.find("Clone", "push button").click() - lib.utils.check(lambda: not win.showing) + win.find("Clone", "push button").click() + lib.utils.check(lambda: not win.showing) - # Check path was generated correctly - win = self._open_window("test-clone-simple-clone") - rows = self._get_all_rows(win) - assert len(rows) == 1 - assert rows[0].is_clone_requested - rows[0].check_in_text("test-clone-simple-clone.img") + # Check path was generated correctly + win = app.manager_open_clone("test-clone-simple-clone") + rows = _get_all_rows(win) + assert len(rows) == 1 + assert rows[0].is_clone_requested + rows[0].check_in_text("test-clone-simple-clone.img") - # Share storage and deal with warnings - rows[0].chkcell.click() - rows[0].check_in_text("Share disk with") - # Do 'cancel' first - win.find("Clone", "push button").click() - self.app.click_alert_button("cause data to be overwritten", "Cancel") - lib.utils.check(lambda: win.active) - win.find("Clone", "push button").click() - self.app.click_alert_button("cause data to be overwritten", "OK") - lib.utils.check(lambda: not win.active) + # Share storage and deal with warnings + rows[0].chkcell.click() + rows[0].check_in_text("Share disk with") + # Do 'cancel' first + win.find("Clone", "push button").click() + app.click_alert_button("cause data to be overwritten", "Cancel") + lib.utils.check(lambda: win.active) + win.find("Clone", "push button").click() + app.click_alert_button("cause data to be overwritten", "OK") + lib.utils.check(lambda: not win.active) - # Verify the new VM shared storage - win = self._open_window("test-clone-simple-clone1") - rows = self._get_all_rows(win) - assert len(rows) == 1 - rows[0].check_in_text("test-clone-simple-clone.img") - - def testCloneMulti(self): - # Clone 'test-clone', check some results, make sure clone works - win = self._open_window("test-clone\n") - win.find("Clone", "push button").click() - lib.utils.check(lambda: not win.showing) - self.app.topwin.find("test-clone1", "table cell") - - # Check test-many-devices which will not work, but confirm - # it errors gracefully - self.app.topwin.find("test-many-devices").click() - sbutton = self.app.topwin.find("Shut Down", "push button") - sbutton.click() - lib.utils.check(lambda: not sbutton.sensitive) - self.app.sleep(.5) - win = self._open_window("test-many-devices") - win.find("Clone", "push button").click() - self.app.click_alert_button("No such file or", "Close") - win.keyCombo("F4") - lib.utils.check(lambda: not win.showing) - - def testCloneStorageChange(self): - # Disable predictable so UUID generation doesn't collide - uri = tests.utils.URIs.test_full.replace(",predictable", "") - self.app.uri = uri - - # Trigger some error handling scenarios - win = self._open_window("test-clone-simple") - newname = "test-aaabbb" - win.find("Name:", "text").set_text(newname) - win.find("Clone", "push button").click() - lib.utils.check(lambda: not win.showing) - - win = self._open_window(newname) - row = self._get_all_rows(win)[0] - row.check_in_text(newname) - oldnewname = newname - newname = "test-aaazzzzbbb" - win.find("Name:", "text").set_text(newname) - row.select() - - win.find("Details", "push button").click() - stgwin = self.app.root.find("Change storage path", "dialog") - pathtxt = stgwin.find(None, "text", "New Path:") - lib.utils.check(lambda: newname in pathtxt.text) - stgwin.find("Browse", "push button").click() - self.app.select_storagebrowser_volume("default-pool", "iso-vol") - lib.utils.check(lambda: "iso-vol" in pathtxt.text) - stgwin.find("OK").click() - self.app.click_alert_button("overwrite the existing", "No") - lib.utils.check(lambda: stgwin.showing) - stgwin.find("OK").click() - self.app.click_alert_button("overwrite the existing", "Yes") - lib.utils.check(lambda: not stgwin.showing) - # Can't clone onto existing storage volume - win.find("Clone", "push button").click() - self.app.click_alert_button(".*Clone onto existing.*", "Close") - - # Reopen dialog and request to share it - win.find("Details", "push button").click() - stgwin = self.app.root.find("Change storage path", "dialog") - chkbox = stgwin.find("Create a new", "check") - lib.utils.check(lambda: chkbox.checked) - chkbox.click() - - # Cancel and reopen, confirm changes didn't stick - stgwin.find("Cancel").click() - lib.utils.check(lambda: not stgwin.showing) - win.find("Details", "push button").click() - stgwin = self.app.root.find("Change storage path", "dialog") - chkbox = stgwin.find("Create a new", "check") - lib.utils.check(lambda: chkbox.checked) - # Requesting sharing again and exit - chkbox.click() - stgwin.find("OK").click() - lib.utils.check(lambda: not stgwin.active) - - # Finish install, verify storage was shared - win.find("Clone", "push button").click() - self.app.click_alert_button("cause data to be overwritten", "OK") - lib.utils.check(lambda: not win.active) - win = self._open_window(newname) - row = self._get_all_rows(win)[0].check_in_text(oldnewname) + # Verify the new VM shared storage + win = app.manager_open_clone("test-clone-simple-clone1") + rows = _get_all_rows(win) + assert len(rows) == 1 + rows[0].check_in_text("test-clone-simple-clone.img") - def testCloneError(self): - # Trigger some error handling scenarios - win = self._open_window("test-clone-full\n") - win.find("Clone", "push button").click() - self.app.click_alert_button("not enough free space", "Close") - lib.utils.check(lambda: win.showing) - win.keyCombo("F4") +def testCloneMulti(app): + # Clone 'test-clone', check some results, make sure clone works + manager = app.topwin + manager.window_maximize() + win = app.manager_open_clone("test-clone") + win.find("Clone", "push button").click() + lib.utils.check(lambda: not win.showing) + app.topwin.find("test-clone1", "table cell") - win = self._open_window("test-clone-simple") - badname = "test/foo" - win.find("Name:", "text").set_text(badname) - rows = self._get_all_rows(win) - rows[0].chkcell.click() - rows[0].check_in_text("Share disk with") - win.find("Clone", "push button").click() - win.find("Clone", "push button").click() - self.app.click_alert_button("cause data to be overwritten", "OK") - self.app.click_alert_button(badname, "Close") - lib.utils.check(lambda: win.active) + # Check test-many-devices which will not work, but confirm + # it errors gracefully + app.topwin.find("test-many-devices").click() + sbutton = app.topwin.find("Shut Down", "push button") + sbutton.click() + lib.utils.check(lambda: not sbutton.sensitive) + app.sleep(.5) + win = app.manager_open_clone("test-many-devices") + win.find("Clone", "push button").click() + app.click_alert_button("No such file or", "Close") + + # Ensure disconnecting will close the dialog + manager.click_title() + app.manager_conn_disconnect("test testdriver.xml") + lib.utils.check(lambda: not win.showing) - def testCloneNonmanaged(self): - # Verify unmanaged clone actual works - import tempfile - tmpsrc = tempfile.NamedTemporaryFile() - tmpdst = tempfile.NamedTemporaryFile() +def testCloneStorageChange(app): + # Disable predictable so UUID generation doesn't collide + uri = tests.utils.URIs.test_full.replace(",predictable", "") + app.uri = uri - open(tmpsrc.name, "w").write(__file__) + # Trigger some error handling scenarios + win = app.manager_open_clone("test-clone-simple") + newname = "test-aaabbb" + win.find("Name:", "text").set_text(newname) + win.find("Clone", "push button").click() + lib.utils.check(lambda: not win.showing) - self.app.open(xmleditor_enabled=True) - manager = self.app.topwin + win = app.manager_open_clone(newname) + row = _get_all_rows(win)[0] + row.check_in_text(newname) + oldnewname = newname + newname = "test-aaazzzzbbb" + win.find("Name:", "text").set_text(newname) + row.select() - win = self.app.open_details_window("test-clone-simple") - win.find("IDE Disk 1", "table cell").click() - win.find("XML", "page tab").click() - xmleditor = win.find("XML editor") - origpath = "/dev/default-pool/test-clone-simple.img" - newpath = tmpsrc.name - xmleditor.set_text(xmleditor.text.replace(origpath, newpath)) - win.find("config-apply").click() - win.find("Details", "page tab").click() - disksrc = win.find("disk-source-path") - lib.utils.check(lambda: disksrc.text == newpath) - win.keyCombo("F4") - lib.utils.check(lambda: not win.active) + win.find("Details", "push button").click() + stgwin = app.root.find("Change storage path", "dialog") + pathtxt = stgwin.find(None, "text", "New Path:") + lib.utils.check(lambda: newname in pathtxt.text) + stgwin.find("Browse", "push button").click() + app.select_storagebrowser_volume("default-pool", "iso-vol") + lib.utils.check(lambda: "iso-vol" in pathtxt.text) + stgwin.find("OK").click() + app.click_alert_button("overwrite the existing", "No") + lib.utils.check(lambda: stgwin.showing) + stgwin.find("OK").click() + app.click_alert_button("overwrite the existing", "Yes") + lib.utils.check(lambda: not stgwin.showing) + # Can't clone onto existing storage volume + win.find("Clone", "push button").click() + app.click_alert_button(".*Clone onto existing.*", "Close") - lib.utils.check(lambda: manager.active) - win = self._open_window("test-clone-simple") - row = self._get_all_rows(win)[0] - row.check_in_text(tmpsrc.name) - row.select() + # Reopen dialog and request to share it + win.find("Details", "push button").click() + stgwin = app.root.find("Change storage path", "dialog") + chkbox = stgwin.find("Create a new", "check") + lib.utils.check(lambda: chkbox.checked) + chkbox.click() - win.find("Details", "push button").click() - stgwin = self.app.root.find("Change storage path", "dialog") - pathtxt = stgwin.find(None, "text", "New Path:") - os.unlink(tmpdst.name) - pathtxt.set_text(tmpdst.name) - stgwin.find("OK").click() - win.find("Clone", "push button").click() - lib.utils.check(lambda: not win.active) - lib.utils.check(lambda: os.path.exists(tmpdst.name)) + # Cancel and reopen, confirm changes didn't stick + stgwin.find("Cancel").click() + lib.utils.check(lambda: not stgwin.showing) + win.find("Details", "push button").click() + stgwin = app.root.find("Change storage path", "dialog") + chkbox = stgwin.find("Create a new", "check") + lib.utils.check(lambda: chkbox.checked) + # Requesting sharing again and exit + chkbox.click() + stgwin.find("OK").click() + lib.utils.check(lambda: not stgwin.active) - assert open(tmpsrc.name).read() == open(tmpdst.name).read() + # Finish install, verify storage was shared + win.find("Clone", "push button").click() + app.click_alert_button("cause data to be overwritten", "OK") + lib.utils.check(lambda: not win.active) + win = app.manager_open_clone(newname) + row = _get_all_rows(win)[0].check_in_text(oldnewname) + + + +def testCloneError(app): + # Trigger some error handling scenarios + win = app.manager_open_clone("test-clone-full") + win.find("Clone", "push button").click() + app.click_alert_button("not enough free space", "Close") + lib.utils.check(lambda: win.showing) + win.keyCombo("F4") + + win = app.manager_open_clone("test-clone-simple") + badname = "test/foo" + win.find("Name:", "text").set_text(badname) + rows = _get_all_rows(win) + rows[0].chkcell.click() + rows[0].check_in_text("Share disk with") + win.find("Clone", "push button").click() + win.find("Clone", "push button").click() + app.click_alert_button("cause data to be overwritten", "OK") + app.click_alert_button(badname, "Close") + lib.utils.check(lambda: win.active) + + + +def testCloneNonmanaged(app): + # Verify unmanaged clone actual works + import tempfile + tmpsrc = tempfile.NamedTemporaryFile() + tmpdst = tempfile.NamedTemporaryFile() + + open(tmpsrc.name, "w").write(__file__) + + app.open(xmleditor_enabled=True) + manager = app.topwin + + win = app.manager_open_details("test-clone-simple") + win.find("IDE Disk 1", "table cell").click() + win.find("XML", "page tab").click() + xmleditor = win.find("XML editor") + origpath = "/dev/default-pool/test-clone-simple.img" + newpath = tmpsrc.name + xmleditor.set_text(xmleditor.text.replace(origpath, newpath)) + win.find("config-apply").click() + win.find("Details", "page tab").click() + disksrc = win.find("disk-source-path") + lib.utils.check(lambda: disksrc.text == newpath) + win.keyCombo("F4") + lib.utils.check(lambda: not win.active) + + lib.utils.check(lambda: manager.active) + win = app.manager_open_clone("test-clone-simple") + row = _get_all_rows(win)[0] + row.check_in_text(tmpsrc.name) + row.select() + + win.find("Details", "push button").click() + stgwin = app.root.find("Change storage path", "dialog") + pathtxt = stgwin.find(None, "text", "New Path:") + os.unlink(tmpdst.name) + pathtxt.set_text(tmpdst.name) + stgwin.find("OK").click() + win.find("Clone", "push button").click() + lib.utils.check(lambda: not win.active) + lib.utils.check(lambda: os.path.exists(tmpdst.name)) + + assert open(tmpsrc.name).read() == open(tmpdst.name).read() diff --git a/tests/uitests/test_connection.py b/tests/uitests/test_connection.py index 790e02c9..5ef45c40 100644 --- a/tests/uitests/test_connection.py +++ b/tests/uitests/test_connection.py @@ -4,109 +4,97 @@ from . import lib -class UITestConnection(lib.testcase.UITestCase): - """ - UI tests for various connection.py related bits - """ +################################################### +# UI tests for various connection.py related bits # +################################################### - ############## - # Test cases # - ############## +def testConnectionBlacklist(app): + app.open( + extra_opts=["--test-options=object-blacklist=test-many-devices"]) + manager = app.topwin - def testConnectionBlacklist(self): - self.app.open( - extra_opts=["--test-options=object-blacklist=test-many-devices"]) - manager = self.app.topwin - - def _delete_vm(vmname): - cell = manager.find(vmname, "table cell") - cell.click() - cell.click(button=3) - menu = self.app.root.find("vm-action-menu") - menu.find("Delete", "menu item").click() - delete = self.app.root.find_fuzzy("Delete", "frame") - delete.find("Delete associated", "check box").click() - delete.find("Delete", "push button").click() - lib.utils.check(lambda: cell.dead) - lib.utils.check(lambda: manager.active) - - lib.utils.check( - lambda: "test-many-devices" not in self.app.topwin.fmt_nodes()) - _delete_vm("test-arm-kernel") - _delete_vm("test alternate") - _delete_vm("test-clone-simple") - self.app.sleep(.5) - lib.utils.check( - lambda: "test-many-devices" not in self.app.topwin.fmt_nodes()) - - def testConnectionConnCrash(self): - self.app.open( - extra_opts=["--test-options=conn-crash"]) - manager = self.app.topwin - - self.app.sleep(1) - manager.find(r"^test testdriver.xml - Not Connected", "table cell") + def _delete_vm(vmname): + app.manager_vm_action(vmname, delete=True) + delete = app.find_window("Delete") + delete.find("Delete associated", "check box").click() + delete.find("Delete", "push button").click() lib.utils.check(lambda: manager.active) - def testConnectionFakeEvents(self): - self.app.open( - extra_opts=["--test-options=fake-nodedev-event=computer", - "--test-options=fake-agent-event=test-many-devices"]) - manager = self.app.topwin - self.app.sleep(2.5) - lib.utils.check(lambda: manager.active) - - def testConnectionOpenauth(self): - self.app.open( - extra_opts=["--test-options=fake-openauth"], - window_name="Authentication required") - - dialog = self.app.root.find("Authentication required") - def _run(): - username = dialog.find("Username:.*entry") - password = dialog.find("Password:.*entry") - username.click() - username.text = "foo" - self.app.rawinput.pressKey("Enter") - lib.utils.check(lambda: password.focused) - password.typeText("bar") + lib.utils.check( + lambda: "test-many-devices" not in app.topwin.fmt_nodes()) + _delete_vm("test-arm-kernel") + _delete_vm("test-clone-full") + _delete_vm("test-clone-simple") + app.sleep(.5) + lib.utils.check( + lambda: "test-many-devices" not in app.topwin.fmt_nodes()) - _run() - dialog.find("OK", "push button").click() - lib.utils.check(lambda: not dialog.showing) - manager = self.app.root.find("Virtual Machine Manager", "frame") - manager.find("^test testdriver.xml$", "table cell") +def testConnectionConnCrash(app): + app.open( + extra_opts=["--test-options=conn-crash"]) + manager = app.topwin - # Disconnect and reconnect to trigger it again - def _retrigger_connection(): - manager.click() - c = manager.find_fuzzy("testdriver.xml", "table cell") - c.click() - c.click(button=3) - self.app.root.find("conn-disconnect", "menu item").click() - manager.click() - c = manager.find_fuzzy("testdriver.xml", "table cell") - c.click() - c.click(button=3) - self.app.root.find("conn-connect", "menu item").click() + app.sleep(1) + manager.find(r"^test testdriver.xml - Not Connected", "table cell") + lib.utils.check(lambda: manager.active) - _retrigger_connection() - dialog = self.app.root.find("Authentication required") - _run() - self.app.rawinput.pressKey("Enter") - lib.utils.check(lambda: not dialog.showing) - manager = self.app.root.find("Virtual Machine Manager", "frame") - manager.find("^test testdriver.xml$", "table cell") - _retrigger_connection() - dialog = self.app.root.find("Authentication required") - dialog.find("Cancel", "push button").click() - lib.utils.check(lambda: not dialog.showing) - self.app.click_alert_button("Unable to connect", "Close") - manager.find("test testdriver.xml - Not Connected", "table cell") +def testConnectionFakeEvents(app): + app.open( + extra_opts=["--test-options=fake-nodedev-event=computer", + "--test-options=fake-agent-event=test-many-devices"]) + manager = app.topwin + app.sleep(2.5) + lib.utils.check(lambda: manager.active) - def testConnectionSessionError(self): - self.app.open( - extra_opts=["--test-options=fake-session-error"]) - self.app.click_alert_button("Could not detect a local session", "Close") + +def testConnectionOpenauth(app): + app.open( + extra_opts=["--test-options=fake-openauth"], + window_name="Authentication required") + + dialog = app.root.find("Authentication required") + def _run(): + username = dialog.find("Username:.*entry") + password = dialog.find("Password:.*entry") + username.click() + username.text = "foo" + app.rawinput.pressKey("Enter") + lib.utils.check(lambda: password.focused) + password.typeText("bar") + + + _run() + dialog.find("OK", "push button").click() + lib.utils.check(lambda: not dialog.showing) + manager = app.find_window("Virtual Machine Manager") + manager.find("^test testdriver.xml$", "table cell") + + # Disconnect and reconnect to trigger it again + def _retrigger_connection(): + manager.click() + app.manager_conn_disconnect("test testdriver.xml") + manager.click() + app.manager_conn_connect("test testdriver.xml") + + _retrigger_connection() + dialog = app.root.find("Authentication required") + _run() + app.rawinput.pressKey("Enter") + lib.utils.check(lambda: not dialog.showing) + manager = app.find_window("Virtual Machine Manager") + manager.find("^test testdriver.xml$", "table cell") + + _retrigger_connection() + dialog = app.root.find("Authentication required") + dialog.find("Cancel", "push button").click() + lib.utils.check(lambda: not dialog.showing) + app.click_alert_button("Unable to connect", "Close") + manager.find("test testdriver.xml - Not Connected", "table cell") + + +def testConnectionSessionError(app): + app.open( + extra_opts=["--test-options=fake-session-error"]) + app.click_alert_button("Could not detect a local session", "Close") diff --git a/tests/uitests/test_createconn.py b/tests/uitests/test_createconn.py index a0ba7676..eb69ebee 100644 --- a/tests/uitests/test_createconn.py +++ b/tests/uitests/test_createconn.py @@ -4,144 +4,100 @@ from . import lib -class VMMConnect(lib.testcase.UITestCase): - """ - UI tests for the 'open connection' dialog - """ +############################################# +# UI tests for the 'open connection' dialog # +############################################# - ############## - # Test cases # - ############## - def testConnect(self): - # Start with connection delete - c = self.app.root.find("test testdriver.xml", "table cell") - c.click(button=3) - self.app.root.find("conn-disconnect", "menu item").click() - lib.utils.check(lambda: "Not Connected" in c.text) - c.click(button=3) - self.app.root.find("conn-delete", "menu item").click() - self.app.click_alert_button("will remove the connection", "No") - lib.utils.check(lambda: not c.dead) - c.click(button=3) - self.app.root.find("conn-delete", "menu item").click() - self.app.click_alert_button("will remove the connection", "Yes") - lib.utils.check(lambda: c.dead) +def testConnect(app): + # Start with connection delete + c = app.manager_conn_disconnect("test testdriver.xml") + c.click(button=3) + app.root.find("conn-delete", "menu item").click() + app.click_alert_button("will remove the connection", "No") + lib.utils.check(lambda: not c.dead) + app.manager_conn_delete("test testdriver.xml") - # Launch the dialog, grab some UI pointers - self.app.root.find("File", "menu").click() - self.app.root.find("Add Connection...", "menu item").click() - win = self.app.root.find_fuzzy("Add Connection", "dialog") + # Launch the dialog, grab some UI pointers + win = app.manager_open_createconn() + connect = win.find("Connect", "push button") + remote = win.find_fuzzy("Connect to remote", "check box") + user = win.find("Username", "text") + host = win.find("Hostname", "text") + urilabel = win.find("uri-label", "label") + lib.utils.check(lambda: user.showing is host.showing is True) - connect = win.find("Connect", "push button") - remote = win.find_fuzzy("Connect to remote", "check box") - user = win.find("Username", "text") - host = win.find("Hostname", "text") - urilabel = win.find("uri-label", "label") - urientry = win.find("uri-entry", "text") - lib.utils.check(lambda: user.showing is host.showing is True) + # Select all HV options + win.combo_select("Hypervisor", "QEMU/KVM user session") + win.combo_select("Hypervisor", r"^QEMU/KVM$") + win.combo_select("Hypervisor", "Xen") + win.combo_select("Hypervisor", "Bhyve") + win.combo_select("Hypervisor", "Virtuozzo") + win.combo_select("Hypervisor", r".*LXC.*") - # Select all HV options - win.combo_select("Hypervisor", "QEMU/KVM user session") - win.combo_select("Hypervisor", r"^QEMU/KVM$") - win.combo_select("Hypervisor", "Xen") - win.combo_select("Hypervisor", "Bhyve") - win.combo_select("Hypervisor", "Virtuozzo") - win.combo_select("Hypervisor", r".*LXC.*") + # Test a simple selection + win.combo_select("Hypervisor", "QEMU/KVM user session") + lib.utils.check(lambda: user.showing is host.showing is False) + lib.utils.check(lambda: urilabel.text == "qemu:///session") - # Test a simple selection - win.combo_select("Hypervisor", "QEMU/KVM user session") - lib.utils.check(lambda: user.showing is host.showing is False) - lib.utils.check(lambda: urilabel.text == "qemu:///session") + # Cancel the dialog + win.find_fuzzy("Cancel", "push button").click() + lib.utils.check(lambda: not win.showing) - # Cancel the dialog - win.find_fuzzy("Cancel", "push button").click() - lib.utils.check(lambda: not win.showing) + # Reopen it, confirm content changed + win = app.manager_open_createconn() + lib.utils.check(lambda: ":///session" not in urilabel.text) - # Reopen it, confirm content changed - self.app.root.find("File", "menu").click() - self.app.root.find("Add Connection...", "menu item").click() - win = self.app.root.find_fuzzy("Add Connection", "dialog") - lib.utils.check(lambda: ":///session" not in urilabel.text) + # Relaunch the dialog, confirm it doesn't overwrite content + win.combo_select("Hypervisor", ".*LXC.*") + lib.utils.check(lambda: "lxc" in urilabel.text) + win = app.manager_open_createconn() + lib.utils.check(lambda: win.active) + lib.utils.check(lambda: "lxc" in urilabel.text) - # Relaunch the dialog, confirm it doesn't overwrite content - win.combo_select("Hypervisor", ".*LXC.*") - lib.utils.check(lambda: "lxc" in urilabel.text) - self.app.root.find("File", "menu").click() - self.app.root.find("Add Connection...", "menu item").click() - lib.utils.check(lambda: win.active) - lib.utils.check(lambda: "lxc" in urilabel.text) + # Enter a failing URI, make sure error is raised, and we can + # fall back to the dialog + win.combo_select("Hypervisor", "Xen") + remote.click() + user.set_text("fribuser") + connect.click() + app.click_alert_button("hostname is required", "OK") + fakeipv6 = "fe80::1" + host.set_text(fakeipv6) + lib.utils.check(lambda: urilabel.text == "xen+ssh://fribuser@[%s]/" % fakeipv6) + fakehost = "ix8khfyidontexistkdjur.com" + host.set_text(fakehost + ":12345") + lib.utils.check(lambda: urilabel.text == "xen+ssh://fribuser@%s:12345/" % fakehost) + connect.click() - # Enter a failing URI, make sure error is raised, and we can - # fall back to the dialog - win.combo_select("Hypervisor", "Xen") - remote.click() - user.set_text("fribuser") - connect.click() - self.app.click_alert_button("hostname is required", "OK") - fakeipv6 = "fe80::1" - host.set_text(fakeipv6) - lib.utils.check(lambda: urilabel.text == "xen+ssh://fribuser@[%s]/" % fakeipv6) - fakehost = "ix8khfyidontexistkdjur.com" - host.set_text(fakehost + ":12345") - lib.utils.check(lambda: urilabel.text == "xen+ssh://fribuser@%s:12345/" % fakehost) - connect.click() + lib.utils.check(lambda: win.showing is True) + c = app.root.find_fuzzy(fakehost, "table cell") + lib.utils.check(lambda: "Connecting..." not in c.text, timeout=10) + app.click_alert_button("Unable to connect", "No") - lib.utils.check(lambda: win.showing is True) - c = self.app.root.find_fuzzy(fakehost, "table cell") - lib.utils.check(lambda: "Connecting..." not in c.text, timeout=10) - self.app.click_alert_button("Unable to connect", "No") + # Ensure dialog shows old contents for editing + lib.utils.check(lambda: win.showing) + lib.utils.check(lambda: fakehost in host.text) - # Ensure dialog shows old contents for editing - lib.utils.check(lambda: win.showing) - lib.utils.check(lambda: fakehost in host.text) + # This time say 'yes' + connect.click() + lib.utils.check(lambda: win.showing is True) + c = app.root.find_fuzzy(fakehost, "table cell") + lib.utils.check(lambda: "Connecting..." not in c.text, timeout=10) + app.click_alert_button("Unable to connect", "Yes") + c = app.root.find_fuzzy(fakehost, "table cell") + lib.utils.check(lambda: win.showing is False) - # This time say 'yes' - connect.click() - lib.utils.check(lambda: win.showing is True) - c = self.app.root.find_fuzzy(fakehost, "table cell") - lib.utils.check(lambda: "Connecting..." not in c.text, timeout=10) - self.app.click_alert_button("Unable to connect", "Yes") - c = self.app.root.find_fuzzy(fakehost, "table cell") + # Test with custom test:///default connection + app.manager_createconn("test:///default") + # Do it again to make sure things don't explode + app.manager_createconn("test:///default") - # Test with custom test:///default connection - lib.utils.check(lambda: win.showing is False) - self.app.root.find("File", "menu").click() - self.app.root.find("Add Connection...", "menu item").click() - win = self.app.root.find_fuzzy("Add Connection", "dialog") - win.combo_select("Hypervisor", "Custom URI") - urientry.set_text("test:///default") - connect.click() + # Test connection double click + c = app.manager_conn_disconnect("test default") + c.doubleClick() + lib.utils.check(lambda: "Not Connected" not in c.text) - # Do it again to make sure things don't explode - lib.utils.check(lambda: win.showing is False) - self.app.root.find("File", "menu").click() - self.app.root.find("Add Connection...", "menu item").click() - win = self.app.root.find_fuzzy("Add Connection", "dialog") - win.combo_select("Hypervisor", "Custom URI") - urientry.set_text("test:///default") - connect.click() - - # Try various connect/disconnect routines - lib.utils.check(lambda: win.showing is False) - c = self.app.root.find("test default", "table cell") - c.click(button=3) - self.app.root.find("conn-disconnect", "menu item").click() - lib.utils.check(lambda: "Not Connected" in c.text) - c.click(button=3) - self.app.root.find("conn-connect", "menu item").click() - c = self.app.root.find("test default", "table cell") - c.click(button=3) - self.app.root.find("conn-disconnect", "menu item").click() - lib.utils.check(lambda: "Not Connected" in c.text) - c.doubleClick() - c = self.app.root.find("test default", "table cell") - c.click() - # Delete it - c.click(button=3) - self.app.root.find("conn-disconnect", "menu item").click() - lib.utils.check(lambda: "Not Connected" in c.text) - c.click(button=3) - self.app.root.find("conn-delete", "menu item").click() - self.app.click_alert_button("will remove the connection", "Yes") - lib.utils.check(lambda: c.dead) + # Delete it + app.manager_conn_disconnect("test default") + app.manager_conn_delete("test default") diff --git a/tests/uitests/test_createnet.py b/tests/uitests/test_createnet.py index 761bcdea..99526358 100644 --- a/tests/uitests/test_createnet.py +++ b/tests/uitests/test_createnet.py @@ -4,169 +4,166 @@ from . import lib -class CreateNet(lib.testcase.UITestCase): +##################################### +# UI tests for the createnet wizard # +##################################### + +def _open_netadd(app, hostwin): + hostwin.find("net-add", "push button").click() + win = app.find_window("Create a new virtual network") + return win + + +def testCreateNet(app): """ - UI tests for the createnet wizard + Basic test with object state management afterwards """ + hostwin = app.manager_open_host("Virtual Networks") + win = _open_netadd(app, hostwin) - def _open_create_win(self, hostwin): - hostwin.find("net-add", "push button").click() - win = self.app.root.find( - "Create a new virtual network", "frame") - return win + # Create a simple default network + name = win.find("Name:", "text") + finish = win.find("Finish", "push button") + lib.utils.check(lambda: name.text == "network") + newname = "a-test-new-net" + name.set_text(newname) + finish.click() + + # Select the new network in the host window, then do + # stop->start->stop->delete, for lifecycle testing + lib.utils.check(lambda: hostwin.active) + cell = hostwin.find(newname, "table cell") + delete = hostwin.find("net-delete", "push button") + start = hostwin.find("net-start", "push button") + stop = hostwin.find("net-stop", "push button") + + cell.click() + stop.click() + lib.utils.check(lambda: start.sensitive) + start.click() + lib.utils.check(lambda: stop.sensitive) + stop.click() + lib.utils.check(lambda: delete.sensitive) + + # Delete it, clicking No first + delete.click() + app.click_alert_button("permanently delete the network", "No") + lib.utils.check(lambda: not cell.dead) + delete.click() + app.click_alert_button("permanently delete the network", "Yes") + # Ensure it's gone + lib.utils.check(lambda: cell.dead) - ############## - # Test cases # - ############## - def testCreateNet(self): - """ - Basic test with object state management afterwards - """ - hostwin = self.app.open_host_window("Virtual Networks") - win = self._open_create_win(hostwin) +def testCreateNetXMLEditor(app): + """ + Test the XML editor + """ + app.open(xmleditor_enabled=True) + hostwin = app.manager_open_host("Virtual Networks") + win = _open_netadd(app, hostwin) + name = win.find("Name:", "text") + finish = win.find("Finish", "push button") - # Create a simple default network - name = win.find("Name:", "text") - finish = win.find("Finish", "push button") - lib.utils.check(lambda: name.text == "network") - newname = "a-test-new-net" - name.set_text(newname) - finish.click() + # Create a new obj with XML edited name, verify it worked + tmpname = "objtmpname" + newname = "froofroo" + name.set_text(tmpname) + win.find("XML", "page tab").click() + xmleditor = win.find("XML editor") + newtext = xmleditor.text.replace(">%s<" % tmpname, ">%s<" % newname) + xmleditor.set_text(newtext) + finish.click() + lib.utils.check(lambda: hostwin.active) + cell = hostwin.find(newname, "table cell") + cell.click() - # Select the new network in the host window, then do - # stop->start->stop->delete, for lifecycle testing - lib.utils.check(lambda: hostwin.active) - cell = hostwin.find(newname, "table cell") - delete = hostwin.find("net-delete", "push button") - start = hostwin.find("net-start", "push button") - stop = hostwin.find("net-stop", "push button") + # Do standard xmleditor tests + win = _open_netadd(app, hostwin) + lib.utils.test_xmleditor_interactions(app, win, finish) + win.find("Cancel", "push button").click() + lib.utils.check(lambda: not win.visible) - cell.click() - stop.click() - lib.utils.check(lambda: start.sensitive) - start.click() - lib.utils.check(lambda: stop.sensitive) - stop.click() - lib.utils.check(lambda: delete.sensitive) - - # Delete it, clicking No first - delete.click() - self.app.click_alert_button("permanently delete the network", "No") - lib.utils.check(lambda: not cell.dead) - delete.click() - self.app.click_alert_button("permanently delete the network", "Yes") - # Ensure it's gone - lib.utils.check(lambda: cell.dead) + # Ensure host window closes fine + hostwin.click() + hostwin.keyCombo("w") + lib.utils.check(lambda: not hostwin.showing and + not hostwin.active) - def testCreateNetXMLEditor(self): - """ - Test the XML editor - """ - self.app.open(xmleditor_enabled=True) - hostwin = self.app.open_host_window("Virtual Networks") - win = self._open_create_win(hostwin) - name = win.find("Name:", "text") - finish = win.find("Finish", "push button") +def testCreateNetMulti(app): + """ + Test remaining create options + """ + app.uri = "test:///default" + hostwin = app.manager_open_host( + "Virtual Networks", conn_label="test default") + win = _open_netadd(app, hostwin) + finish = win.find("Finish", "push button") - # Create a new obj with XML edited name, verify it worked - tmpname = "objtmpname" - newname = "froofroo" - name.set_text(tmpname) - win.find("XML", "page tab").click() - xmleditor = win.find("XML editor") - newtext = xmleditor.text.replace(">%s<" % tmpname, ">%s<" % newname) - xmleditor.set_text(newtext) - finish.click() - lib.utils.check(lambda: hostwin.active) - cell = hostwin.find(newname, "table cell") - cell.click() + # Create a network with a bunch of options + win.find("Name:", "text").set_text("default") + win.find("net-mode").click() + win.find("Isolated", "menu item").click() + win.find("IPv4 configuration").click_expander() + win.find("ipv4-network").set_text("192.168.100.0/25") + ipv4start = win.find("ipv4-start") + ipv4end = win.find("ipv4-end") + lib.utils.check(lambda: ipv4start.text == "192.168.100.64") + lib.utils.check(lambda: ipv4end.text == "192.168.100.126") + win.find("Enable DHCPv4").click() + win.find("Enable IPv4").click() + win.find("IPv6 configuration").click_expander() + win.find("Enable IPv6").click() + win.find("Enable DHCPv6").click() + win.find("ipv6-network").set_text("fd00:beef:10:6::1/64") + win.find("ipv6-start").set_text("fd00:beef:10:6::1:1") + win.find("ipv6-end").set_text("bad") + win.find("DNS domain name").click_expander() + win.find("Custom").click() + win.find("domain-custom").set_text("mydomain") + finish.click() + # Name collision validation + app.click_alert_button("in use by another network", "Close") + win.find("Name:", "text").set_text("newnet1") + finish.click() + # XML define error + app.click_alert_button("Error creating virtual network", "Close") + win.find("ipv6-end").set_text("fd00:beef:10:6::1:f1") + finish.click() + lib.utils.check(lambda: hostwin.active) - # Do standard xmleditor tests - win = self._open_create_win(hostwin) - lib.utils.test_xmleditor_interactions(self.app, win, finish) - win.find("Cancel", "push button").click() - lib.utils.check(lambda: not win.visible) + # More option work + win = _open_netadd(app, hostwin) + win.find("Name:", "text").set_text("newnet2") + devicelist = win.find("net-devicelist") + lib.utils.check(lambda: not devicelist.visible) + win.find("net-mode").click() + win.find("SR-IOV", "menu item").click() + lib.utils.check(lambda: devicelist.visible) + # Just confirm this is here + win.find("No available device", "menu item") + win.find("net-mode").click() + win.find("Routed", "menu item").click() + win.find("net-forward").click() + win.find("Physical device", "menu item").click() + win.find("net-device").set_text("fakedev0") + finish.click() + lib.utils.check(lambda: hostwin.active) - # Ensure host window closes fine - hostwin.click() - hostwin.keyCombo("w") - lib.utils.check(lambda: not hostwin.showing and - not hostwin.active) - def testCreateNetMulti(self): - """ - Test remaining create options - """ - self.app.uri = "test:///default" - hostwin = self.app.open_host_window( - "Virtual Networks", conn_label="test default") - win = self._open_create_win(hostwin) - finish = win.find("Finish", "push button") +def testCreateNetSRIOV(app): + """ + We need the full URI to test the SRIOV method + """ + app.open(xmleditor_enabled=True) + hostwin = app.manager_open_host("Virtual Networks") + win = _open_netadd(app, hostwin) + finish = win.find("Finish", "push button") - # Create a network with a bunch of options - win.find("Name:", "text").set_text("default") - win.find("net-mode").click() - win.find("Isolated", "menu item").click() - win.find("IPv4 configuration").click_expander() - win.find("ipv4-network").set_text("192.168.100.0/25") - ipv4start = win.find("ipv4-start") - ipv4end = win.find("ipv4-end") - lib.utils.check(lambda: ipv4start.text == "192.168.100.64") - lib.utils.check(lambda: ipv4end.text == "192.168.100.126") - win.find("Enable DHCPv4").click() - win.find("Enable IPv4").click() - win.find("IPv6 configuration").click_expander() - win.find("Enable IPv6").click() - win.find("Enable DHCPv6").click() - win.find("ipv6-network").set_text("fd00:beef:10:6::1/64") - win.find("ipv6-start").set_text("fd00:beef:10:6::1:1") - win.find("ipv6-end").set_text("bad") - win.find("DNS domain name").click_expander() - win.find("Custom").click() - win.find("domain-custom").set_text("mydomain") - finish.click() - # Name collision validation - self.app.click_alert_button("in use by another network", "Close") - win.find("Name:", "text").set_text("newnet1") - finish.click() - # XML define error - self.app.click_alert_button("Error creating virtual network", "Close") - win.find("ipv6-end").set_text("fd00:beef:10:6::1:f1") - finish.click() - lib.utils.check(lambda: hostwin.active) - - # More option work - win = self._open_create_win(hostwin) - win.find("Name:", "text").set_text("newnet2") - devicelist = win.find("net-devicelist") - lib.utils.check(lambda: not devicelist.visible) - win.find("net-mode").click() - win.find("SR-IOV", "menu item").click() - lib.utils.check(lambda: devicelist.visible) - # Just confirm this is here - win.find("No available device", "menu item") - win.find("net-mode").click() - win.find("Routed", "menu item").click() - win.find("net-forward").click() - win.find("Physical device", "menu item").click() - win.find("net-device").set_text("fakedev0") - finish.click() - lib.utils.check(lambda: hostwin.active) - - def testCreateNetSRIOV(self): - """ - We need the full URI to test the SRIOV method - """ - self.app.open(xmleditor_enabled=True) - hostwin = self.app.open_host_window("Virtual Networks") - win = self._open_create_win(hostwin) - finish = win.find("Finish", "push button") - - win.find("net-mode").click() - win.find("SR-IOV", "menu item").click() - win.find("net-devicelist").click() - win.find_fuzzy("eth3", "menu item").click() - finish.click() + win.find("net-mode").click() + win.find("SR-IOV", "menu item").click() + win.find("net-devicelist").click() + win.find_fuzzy("eth3", "menu item").click() + finish.click() diff --git a/tests/uitests/test_createpool.py b/tests/uitests/test_createpool.py index 9a172ef9..d70c1589 100644 --- a/tests/uitests/test_createpool.py +++ b/tests/uitests/test_createpool.py @@ -4,157 +4,152 @@ from . import lib -class CreatePool(lib.testcase.UITestCase): - """ - UI tests for the createpool wizard - """ +###################################### +# UI tests for the createpool wizard # +###################################### - def _open_create_win(self, hostwin): - hostwin.find("pool-add", "push button").click() - win = self.app.root.find( - "Add a New Storage Pool", "frame") +def _open_createpool(app, hostwin): + hostwin.find("pool-add", "push button").click() + win = app.find_window("Add a New Storage Pool") + lib.utils.check(lambda: win.active) + return win + + +def testCreatePools(app): + hostwin = app.manager_open_host("Storage") + win = _open_createpool(app, hostwin) + finish = win.find("Finish", "push button") + name = win.find("Name:", "text") + + def _browse_local_path(winlabel, usepath): + chooser = app.root.find(winlabel, "file chooser") + # Enter the filename and select it + chooser.find(usepath, "table cell").click() + obutton = chooser.find("Open", "push button") + lib.utils.check(lambda: obutton.sensitive) + obutton.click() + lib.utils.check(lambda: not chooser.showing) lib.utils.check(lambda: win.active) - return win + + # Create a simple default dir pool + lib.utils.check(lambda: name.text == "pool") + newname = "a-test-new-pool" + name.set_text(newname) + finish.click() + + # Select the new object in the host window, then do + # stop->start->stop->delete, for lifecycle testing + lib.utils.check(lambda: hostwin.active) + cell = hostwin.find(newname, "table cell") + delete = hostwin.find("pool-delete", "push button") + start = hostwin.find("pool-start", "push button") + stop = hostwin.find("pool-stop", "push button") + + cell.click() + stop.click() + lib.utils.check(lambda: start.sensitive) + start.click() + lib.utils.check(lambda: stop.sensitive) + stop.click() + lib.utils.check(lambda: delete.sensitive) + + # Delete it, clicking 'No' first + delete.click() + app.click_alert_button("permanently delete the pool", "No") + lib.utils.check(lambda: not cell.dead) + delete.click() + app.click_alert_button("permanently delete the pool", "Yes") + # Ensure it's gone + lib.utils.check(lambda: cell.dead) + + # Test a disk pool + win = _open_createpool(app, hostwin) + win.combo_select("Type:", "disk:") + newname = "a-disk-pool" + name.set_text("a-disk-pool") + win.find("source-browse").click() + _browse_local_path("Choose source path", "console") + finish.click() + hostwin.find(newname, "table cell") + + # Test a iscsi pool + win = _open_createpool(app, hostwin) + win.combo_select("Type:", "iscsi:") + newname = "a-iscsi-pool" + name.set_text("a-iscsi-pool") + win.find("target-browse").click() + _browse_local_path("Choose target directory", "by-path") + finish.click() + # Catch example error + app.click_alert_button("source host name", "Close") + win.find("Host Name:", "text").set_text("example.com") + win.find("pool-source-path-text").set_text("foo-iqn") + win.find_fuzzy("Initiator IQN:", "check").click() + win.find("iqn-text", "text").set_text("initiator-foo") + finish.click() + hostwin.find(newname, "table cell") + + # Test a logical pool + win = _open_createpool(app, hostwin) + win.combo_select("Type:", "logical:") + newname = "a-lvm-pool" + name.set_text("a-lvm-pool") + + win.combo_check_default("Volgroup", "testvg1") + win.combo_select("Volgroup", "testvg2") + finish.click() + hostwin.find(newname, "table cell") + + # Test a scsi pool + win = _open_createpool(app, hostwin) + win.combo_select("Type:", "scsi:") + newname = "a-scsi-pool" + name.set_text("a-scsi-pool") + win.combo_select("Source Adapter:", "host2") + finish.click() + hostwin.find(newname, "table cell") + + # Test a ceph pool + win = _open_createpool(app, hostwin) + newname = "a-ceph-pool" + name.set_text("a-ceph-pool") + win.combo_select("Type:", "rbd:") + win.find_fuzzy("Host Name:", "text").set_text("example.com:1234") + win.find_fuzzy("pool-source-name-text", "text").typeText("frob") + finish.click() + lib.utils.check(lambda: not win.showing) + lib.utils.check(lambda: hostwin.active) + hostwin.find(newname, "table cell") + + # Ensure host window closes fine + hostwin.click() + hostwin.keyCombo("w") + lib.utils.check(lambda: not hostwin.showing) - ############## - # Test cases # - ############## - def testCreatePools(self): - hostwin = self.app.open_host_window("Storage") - win = self._open_create_win(hostwin) - finish = win.find("Finish", "push button") - name = win.find("Name:", "text") +def testCreatePoolXMLEditor(app): + app.open(xmleditor_enabled=True) + hostwin = app.manager_open_host("Storage") + win = _open_createpool(app, hostwin) + finish = win.find("Finish", "push button") + name = win.find("Name:", "text") - def _browse_local_path(winlabel, usepath): - chooser = self.app.root.find(winlabel, "file chooser") - # Enter the filename and select it - chooser.find(usepath, "table cell").click() - obutton = chooser.find("Open", "push button") - lib.utils.check(lambda: obutton.sensitive) - obutton.click() - lib.utils.check(lambda: not chooser.showing) - lib.utils.check(lambda: win.active) + # Create a new obj with XML edited name, verify it worked + tmpname = "objtmpname" + newname = "froofroo" + name.set_text(tmpname) + win.find("XML", "page tab").click() + xmleditor = win.find("XML editor") + newtext = xmleditor.text.replace(">%s<" % tmpname, ">%s<" % newname) + xmleditor.set_text(newtext) + finish.click() + lib.utils.check(lambda: hostwin.active) + cell = hostwin.find(newname, "table cell") + cell.click() - # Create a simple default dir pool - lib.utils.check(lambda: name.text == "pool") - newname = "a-test-new-pool" - name.set_text(newname) - finish.click() - - # Select the new object in the host window, then do - # stop->start->stop->delete, for lifecycle testing - lib.utils.check(lambda: hostwin.active) - cell = hostwin.find(newname, "table cell") - delete = hostwin.find("pool-delete", "push button") - start = hostwin.find("pool-start", "push button") - stop = hostwin.find("pool-stop", "push button") - - cell.click() - stop.click() - lib.utils.check(lambda: start.sensitive) - start.click() - lib.utils.check(lambda: stop.sensitive) - stop.click() - lib.utils.check(lambda: delete.sensitive) - - # Delete it, clicking 'No' first - delete.click() - self.app.click_alert_button("permanently delete the pool", "No") - lib.utils.check(lambda: not cell.dead) - delete.click() - self.app.click_alert_button("permanently delete the pool", "Yes") - # Ensure it's gone - lib.utils.check(lambda: cell.dead) - - # Test a disk pool - win = self._open_create_win(hostwin) - win.combo_select("Type:", "disk:") - newname = "a-disk-pool" - name.set_text("a-disk-pool") - win.find("source-browse").click() - _browse_local_path("Choose source path", "console") - finish.click() - hostwin.find(newname, "table cell") - - # Test a iscsi pool - win = self._open_create_win(hostwin) - win.combo_select("Type:", "iscsi:") - newname = "a-iscsi-pool" - name.set_text("a-iscsi-pool") - win.find("target-browse").click() - _browse_local_path("Choose target directory", "by-path") - finish.click() - # Catch example error - self.app.click_alert_button("source host name", "Close") - win.find("Host Name:", "text").set_text("example.com") - win.find("pool-source-path-text").set_text("foo-iqn") - win.find_fuzzy("Initiator IQN:", "check").click() - win.find("iqn-text", "text").set_text("initiator-foo") - finish.click() - hostwin.find(newname, "table cell") - - # Test a logical pool - win = self._open_create_win(hostwin) - win.combo_select("Type:", "logical:") - newname = "a-lvm-pool" - name.set_text("a-lvm-pool") - - win.combo_check_default("Volgroup", "testvg1") - win.combo_select("Volgroup", "testvg2") - finish.click() - hostwin.find(newname, "table cell") - - # Test a scsi pool - win = self._open_create_win(hostwin) - win.combo_select("Type:", "scsi:") - newname = "a-scsi-pool" - name.set_text("a-scsi-pool") - win.combo_select("Source Adapter:", "host2") - finish.click() - hostwin.find(newname, "table cell") - - # Test a ceph pool - win = self._open_create_win(hostwin) - newname = "a-ceph-pool" - name.set_text("a-ceph-pool") - win.combo_select("Type:", "rbd:") - win.find_fuzzy("Host Name:", "text").set_text("example.com:1234") - win.find_fuzzy("pool-source-name-text", "text").typeText("frob") - finish.click() - lib.utils.check(lambda: not win.showing) - lib.utils.check(lambda: hostwin.active) - hostwin.find(newname, "table cell") - - # Ensure host window closes fine - hostwin.click() - hostwin.keyCombo("w") - lib.utils.check(lambda: not hostwin.showing) - - - def testCreatePoolXMLEditor(self): - self.app.open(xmleditor_enabled=True) - hostwin = self.app.open_host_window("Storage") - win = self._open_create_win(hostwin) - finish = win.find("Finish", "push button") - name = win.find("Name:", "text") - - # Create a new obj with XML edited name, verify it worked - tmpname = "objtmpname" - newname = "froofroo" - name.set_text(tmpname) - win.find("XML", "page tab").click() - xmleditor = win.find("XML editor") - newtext = xmleditor.text.replace(">%s<" % tmpname, ">%s<" % newname) - xmleditor.set_text(newtext) - finish.click() - lib.utils.check(lambda: hostwin.active) - cell = hostwin.find(newname, "table cell") - cell.click() - - # Do standard xmleditor tests - win = self._open_create_win(hostwin) - lib.utils.test_xmleditor_interactions(self.app, win, finish) - win.find("Cancel", "push button").click() - lib.utils.check(lambda: not win.visible) + # Do standard xmleditor tests + win = _open_createpool(app, hostwin) + lib.utils.test_xmleditor_interactions(app, win, finish) + win.find("Cancel", "push button").click() + lib.utils.check(lambda: not win.visible) diff --git a/tests/uitests/test_createvm.py b/tests/uitests/test_createvm.py index 00a1dc70..8b4b5782 100644 --- a/tests/uitests/test_createvm.py +++ b/tests/uitests/test_createvm.py @@ -3,1259 +3,1279 @@ import unittest.mock +import pytest + import tests from . import lib +################### +# Private helpers # +################### -class NewVM(lib.testcase.UITestCase): +def _open_newvm(app): + app.root.find("New", "push button").click() + return app.find_window("New VM") + + +def _forward(newvm, check=True): + pagenumlabel = newvm.find("pagenum-label") + oldtext = pagenumlabel.text + newvm.find_fuzzy("Forward", "button").click() + if check: + lib.utils.check(lambda: pagenumlabel.text != oldtext) + + +def _back(newvm, check=True): + pagenumlabel = newvm.find("pagenum-label") + oldtext = pagenumlabel.text + newvm.find_fuzzy("Back", "button").click() + if check: + lib.utils.check(lambda: pagenumlabel.text != oldtext) + + +############################################ +# UI tests for virt-manager's NewVM wizard # +############################################ + +def testNewVMMultiConnection(app): """ - UI tests for virt-manager's NewVM wizard + Test the wizard's multiple connection handling """ - - - ################### - # Private helpers # - ################### - - def _open_create_wizard(self): - self.app.root.find("New", "push button").click() - return self.app.root.find("New VM", "frame") - - def forward(self, newvm, check=True): - pagenumlabel = newvm.find("pagenum-label") - oldtext = pagenumlabel.text - newvm.find_fuzzy("Forward", "button").click() - if check: - lib.utils.check(lambda: pagenumlabel.text != oldtext) - - def back(self, newvm, check=True): - pagenumlabel = newvm.find("pagenum-label") - oldtext = pagenumlabel.text - newvm.find_fuzzy("Back", "button").click() - if check: - lib.utils.check(lambda: pagenumlabel.text != oldtext) - - - ############## - # Test cases # - ############## - - def testNewVMMultiConnection(self): - """ - Test the wizard's multiple connection handling - """ - manager = self.app.topwin - - def _add_conn(uri): - manager.find("File", "menu").click() - manager.find("Add Connection...", "menu item").click() - win = self.app.root.find_fuzzy("Add Connection", "dialog") - win.combo_select("Hypervisor", "Custom URI") - win.find("uri-entry", "text").set_text(uri) - win.find("Connect", "push button").click() - - def _stop_conn(txt): - c = manager.find(txt, "table cell") - c.click() - c.click(button=3) - self.app.root.find("conn-disconnect", "menu item").click() - lib.utils.check(lambda: "Not Connected" in c.text) - - # Check the dialog shows 'no connection' error - _stop_conn("test testdriver.xml") - newvm = self._open_create_wizard() - newvm.find_fuzzy("No active connection to install on") - newvm.keyCombo("F4") - lib.utils.check(lambda: manager.active) - - # Check the xen PV only startup warning - def _capsopt(fname): - capsdir = tests.utils.DATADIR + "/capabilities/" - return ",caps=" + capsdir + fname - - # Test empty qemu connection - _add_conn(tests.utils.URIs.kvm + _capsopt("test-empty.xml")) - newvm = self._open_create_wizard() - newvm.find(".*No hypervisor options were found.*KVM kernel modules.*") - newvm.click_title() - newvm.keyCombo("F4") - _stop_conn("QEMU/KVM") - - _add_conn(tests.utils.URIs.kvm_session + - _capsopt("test-qemu-no-kvm.xml")) - newvm = self._open_create_wizard() - newvm.find(".*KVM is not available.*") - newvm.click_title() - newvm.keyCombo("F4") - - _add_conn(tests.utils.URIs.lxc) - _add_conn(tests.utils.URIs.test_full) - _add_conn(tests.utils.URIs.test_default) - - # Open the new VM wizard, select a connection - newvm = self._open_create_wizard() - newvm.combo_select("create-conn", ".*testdriver.xml.*") - self.forward(newvm) - - # Verify media-combo contents for testdriver.xml - cdrom = newvm.find("media-combo") - entry = newvm.find("media-entry") - cdrom.click_combo_entry() - cdrom.find_fuzzy(r"\(/dev/sr1\)") - entry.click() - # Launch this so we can verify storage browser is reset too - newvm.find_fuzzy("install-iso-browse", "button").click() - self.app.select_storagebrowser_volume("default-pool", "iso-vol") - newvm.find_fuzzy("Automatically detect", "check").click() - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(newvm) - - # Back up, select test:///default, verify media-combo is now empty - newvm.click_title() - newvm.keyCombo("F4") - newvm = self._open_create_wizard() - newvm.combo_select("create-conn", ".*test default.*") - self.forward(newvm) - cdrom.click_combo_entry() - lib.utils.check(lambda: "/dev/sr1" not in cdrom.fmt_nodes()) - newvm.find_fuzzy("install-iso-browse", "button").click() - browsewin = self.app.root.find("vmm-storage-browser") - lib.utils.check(lambda: "disk-pool" not in browsewin.fmt_nodes()) - - def testNewVMManualDefault(self): - """ - Click through the New VM wizard with default values + manual, then - delete the VM - """ - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Manual", "radio").click() - self.forward(newvm) - osentry = newvm.find("oslist-entry") - lib.utils.check(lambda: not osentry.text) - - # Make sure we throw an error if no OS selected - self.forward(newvm, check=False) - self.app.click_alert_button("You must select", "OK") - - # Test activating the osentry to grab the popover selection - osentry.click() - osentry.typeText("generic") - newvm.find("oslist-popover") - osentry.click() - self.app.rawinput.pressKey("Enter") - lib.utils.check(lambda: osentry.text == "Generic OS") - - # Verify back+forward still keeps Generic selected - self.app.sleep(.5) - self.back(newvm) - self.app.sleep(.5) - self.forward(newvm) - self.app.sleep(.5) - lib.utils.check(lambda: "Generic" in osentry.text) - osentry.check_onscreen() - - # The sleeps shouldn't be required, but this test continues to be - # flakey, so this is an attempt to fix it. - self.forward(newvm) - self.app.sleep(.5) - self.forward(newvm) - self.app.sleep(.5) - self.forward(newvm) - self.app.sleep(.5) - - - # Empty triggers a specific codepath - newvm.find_fuzzy("Name", "text").set_text("") - # Name collision failure - newvm.find_fuzzy("Name", "text").set_text("test-many-devices") - newvm.find_fuzzy("Finish", "button").click() - self.app.click_alert_button("in use", "OK") - newvm.find_fuzzy("Name", "text").set_text("vm1") - newvm.find_fuzzy("Finish", "button").click() - - # Delete it from the VM window - vmwindow = self.app.root.find_fuzzy("vm1 on", "frame") - vmwindow.find("Virtual Machine", "menu").click() - vmwindow.find("Delete", "menu item").click() - - delete = self.app.root.find_fuzzy("Delete", "frame") - delete.find_fuzzy("Delete", "button").click() - self.app.click_alert_button("Are you sure", "Yes") - - # Verify delete dialog and VM dialog are now gone - lib.utils.check(lambda: vmwindow.showing is False) - - def testNewVMStorage(self): - """ - Test some storage specific paths - """ - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Manual", "radio").click() - self.forward(newvm) - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(newvm) - self.forward(newvm) - - # Trigger size validation failure - sizetext = newvm.find(None, "spin button", "GiB") - sizetext.set_text("10000000") - self.forward(newvm, check=False) - self.app.click_alert_button("Storage parameter error", "OK") - sizetext.set_text("1") - - # Use the storage browser to select a local file - storagetext = newvm.find("storage-entry") - newvm.find_fuzzy("Select or create", "radio").click() - newvm.find("storage-browse").click() - browse = self.app.root.find("vmm-storage-browser") - browse.find("Browse Local", "push button").click() - chooser = self.app.root.find( - "Locate existing storage", "file chooser") - fname = "COPYING" - chooser.find(fname, "table cell").click() - chooser.find("Open", "push button").click() - lib.utils.check(lambda: newvm.active) - lib.utils.check(lambda: "COPYING" in storagetext.text) - - # Start the install - self.forward(newvm) - newvm.find("Finish", "push button").click() - self.app.root.find_fuzzy("vm1 on", "frame") - lib.utils.check(lambda: not newvm.showing) - - - def testNewVMCDROMRegular(self): - """ - Create a new CDROM VM, choosing distro win8, and do some basic - 'Customize before install' before exiting - """ - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Local install media", "radio").click() - self.forward(newvm) - - # check prepopulated cdrom media - combo = newvm.find("media-combo") - combo.click_combo_entry() - combo.find(r"No media detected \(/dev/sr1\)") - combo.find(r"Fedora12_media \(/dev/sr0\)").click() - - # Catch validation error - entry = newvm.find("media-entry") - entry.click() - entry.set_text("") - self.forward(newvm, check=False) - self.app.click_alert_button("media selection is required", "OK") - - # test entry activation too - entry.click() - entry.set_text("/dev/sr0") - self.app.rawinput.pressKey("Enter") - - # Select a fake iso - newvm.find_fuzzy("install-iso-browse", "button").click() - self.app.select_storagebrowser_volume("default-pool", "iso-vol") - - osentry = newvm.find("oslist-entry") - lib.utils.check(lambda: osentry.text == "None detected") - - # Change distro to win8 - newvm.find_fuzzy("Automatically detect", "check").click() - osentry.click() - osentry.set_text("windows 8") - popover = newvm.find("oslist-popover") - popover.check_onscreen() - # Verify Escape resets the text entry - self.app.rawinput.pressKey("Escape") - popover.check_not_onscreen() - lib.utils.check(lambda: osentry.text == "") - # Re-enter text - osentry.set_text("windows 8") - popover.check_onscreen() - popover.find_fuzzy("include-eol").click() - popover.find_fuzzy(r"\(win8\)").click() - popover.check_not_onscreen() - foundtext = osentry.text - # Start typing again, and exit, make sure it resets to previous entry - osentry.click() - osentry.set_text("foo") - popover.check_onscreen() - self.app.rawinput.pressKey("Escape") - popover.check_not_onscreen() - lib.utils.check(lambda: osentry.text == foundtext) - self.forward(newvm) - - # Verify that CPU values are non-default - cpus = newvm.find("cpus", "spin button") - lib.utils.check(lambda: int(cpus.text) > 1, timeout=5) - self.forward(newvm) - self.forward(newvm) - - # Select customize wizard - newvm.find_fuzzy("Customize", "check").click() - newvm.find_fuzzy("Finish", "button").click() - - # Verify CDROM media is inserted - vmwindow = self.app.root.find_fuzzy("win8 on", "frame") - vmwindow.find_fuzzy("IDE CDROM", "table cell").click() - mediaent = vmwindow.find("media-entry") - lib.utils.check(lambda: "iso-vol" in mediaent.text) - - # Change boot autostart - vmwindow.find_fuzzy("Boot", "table cell").click() - vmwindow.find_fuzzy("Start virtual machine", "check").click() - vmwindow.find_fuzzy("config-apply").click() - - # Change to 'copy host CPU' - vmwindow.find_fuzzy("CPUs", "table cell").click() - vmwindow.find_fuzzy("Copy host", "check").click() - vmwindow.find_fuzzy("config-apply").click() - - # Add a default disk - vmwindow.find("add-hardware", "push button").click() - addhw = self.app.root.find("Add New Virtual Hardware", "frame") - addhw.find("Finish", "push button").click() - lib.utils.check(lambda: vmwindow.active) - - # Select the new disk, change the bus to USB - vmwindow.find_fuzzy("IDE Disk 2", "table cell").click() - appl = vmwindow.find("config-apply", "push button") - hwlist = vmwindow.find("hw-list") - tab = vmwindow.find("disk-tab") - tab.find("Disk bus:", "text").set_text("usb") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - # Device is now 'USB Disk 1' - c = hwlist.find("USB Disk 1", "table cell") - lib.utils.check(lambda: c.state_selected) - tab.find("Advanced options", "toggle button").click_expander() - tab.find("Removable:", "check box").click() - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Change NIC mac - vmwindow.find_fuzzy("NIC", "table cell").click() - tab = vmwindow.find("network-tab") - tab.find("mac-entry", "text").set_text("00:11:00:11:00:11") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Start the install, close via the VM window - vmwindow.find_fuzzy("Begin Installation", "button").click() - lib.utils.check(lambda: newvm.showing is False) - vmwindow = self.app.root.find_fuzzy("win8 on", "frame") - vmwindow.find_fuzzy("File", "menu").click() - vmwindow.find_fuzzy("Quit", "menu item").click() - lib.utils.check(lambda: self.app.is_running()) - - def testNewVMCDROMDetect(self): - """ - CDROM with detection - """ - cdrom = tests.utils.DATADIR + "/fakemedia/fake-win7.iso" - newvm = self._open_create_wizard() - newvm.find_fuzzy("Local install media", "radio").click() - self.forward(newvm) - newvm.find("media-entry").click() - newvm.find("media-entry").set_text(cdrom) - # Use forward to trigger detection - self.forward(newvm) - self.forward(newvm) - self.forward(newvm) - newvm.find("Finish", "push button").click() - self.app.root.find_fuzzy("win7 on", "frame") - lib.utils.check(lambda: not newvm.showing) - - - def testNewVMURL(self): - """ - New VM with URL and distro detection, plus having fun with - the storage browser and network selection. - """ - self.app.uri = tests.utils.URIs.kvm - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Network Install", "radio").click() - self.forward(newvm) - osentry = newvm.find("oslist-entry") - lib.utils.check(lambda: osentry.text.startswith("Waiting")) - - newvm.find("install-url-entry").set_text("") - self.forward(newvm, check=False) - self.app.click_alert_button("tree is required", "OK") - - url = "https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/10/Fedora/x86_64/os/" - oslabel = "Fedora 10" - newvm.find("install-url-entry").set_text(url) - newvm.find("install-url-entry").click() - self.app.rawinput.pressKey("Enter") - newvm.find("install-urlopts-expander").click_expander() - newvm.find("install-urlopts-entry").set_text("foo=bar") - - lib.utils.check(lambda: osentry.text == oslabel, timeout=10) - - # Move forward, then back, ensure OS stays selected - self.forward(newvm) - self.back(newvm) - lib.utils.check(lambda: osentry.text == oslabel) - - # Disable autodetect, make sure OS still selected - newvm.find_fuzzy("Automatically detect", "check").click() - lib.utils.check(lambda: osentry.text == oslabel) - self.forward(newvm) - self.back(newvm) - - # Ensure the EOL field was selected - osentry.click() - self.app.rawinput.pressKey("Down") - popover = newvm.find("oslist-popover") - lib.utils.check(lambda: popover.showing) - includeeol = newvm.find("include-eol", "check") - lib.utils.check(lambda: includeeol.isChecked) - - # Re-enable autodetect, check for detecting text - newvm.find_fuzzy("Automatically detect", "check").click() - lib.utils.check(lambda: not popover.showing) - lib.utils.check(lambda: "Detecting" in osentry.text) - lib.utils.check(lambda: osentry.text == oslabel, timeout=10) - - # Progress the install - self.forward(newvm) - self.forward(newvm) - self.forward(newvm) - newvm.find_fuzzy("Finish", "button").click() - - progress = self.app.root.find_fuzzy( - "Creating Virtual Machine", "frame") - lib.utils.check(lambda: not progress.showing, timeout=120) - - details = self.app.root.find_fuzzy("fedora10 on", "frame") - lib.utils.check(lambda: not newvm.showing) - - # Re-run the newvm wizard, check that URL was remembered - details.keyCombo("F4") - newvm = self._open_create_wizard() - newvm.find_fuzzy("Network Install", "radio").click() - self.forward(newvm) - urlcombo = newvm.find("install-url-combo") - lib.utils.check(lambda: urlcombo.showing) - lib.utils.check(lambda: url in urlcombo.fmt_nodes()) - - def testNewKVMQ35Tweaks(self): - """ - New VM that should default to Q35, but tweak things a bunch - """ - self.app.uri = tests.utils.URIs.kvm - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Import", "radio").click() - self.forward(newvm) - newvm.find("import-entry").set_text("/dev/default-pool/testvol1.img") - newvm.find("oslist-entry").set_text("fedora30") - popover = newvm.find("oslist-popover") - popover.find("include-eol").click() - popover.find_fuzzy("Fedora 30").click() - self.forward(newvm) - self.forward(newvm) - - # Select customize wizard, we will use this VM to - # hit some code paths elsewhere - newvm.find_fuzzy("Customize", "check").click() - newvm.find_fuzzy("Finish", "button").click() - vmname = "fedora30" - details = self.app.root.find_fuzzy("%s on" % vmname, "frame") - appl = details.find("config-apply") - - # Tweak some Overview settings - details.combo_check_default("Chipset:", "Q35") - details.combo_check_default("Firmware:", "BIOS") - - # Switch i440FX and back - details.combo_select("Chipset:", "i440FX") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - details.combo_select("Chipset:", "Q35") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - # Switch to UEFI, back to BIOS, back to UEFI - details.combo_select("Firmware:", ".*x86_64.*") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - # Switch back to BIOS - details.combo_select("Firmware:", "BIOS") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - # Switch back to UEFI - details.combo_select("Firmware:", ".*x86_64.*") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Add another network device - details.find("add-hardware", "push button").click() - addhw = self.app.root.find("Add New Virtual Hardware", "frame") - addhw.find("Network", "table cell").click() - tab = addhw.find("network-tab", None) - lib.utils.check(lambda: tab.showing) - addhw.find("Finish", "push button").click() - lib.utils.check(lambda: not addhw.active) - lib.utils.check(lambda: details.active) - - # Finish - details.find_fuzzy("Begin Installation", "button").click() - lib.utils.check(lambda: details.dead) - self.app.root.find_fuzzy("%s on" % vmname, "frame") - - def testNewKVMQ35UEFI(self): - """ - New VM that should default to Q35, and set UEFI - """ - self.app.uri = tests.utils.URIs.kvm - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Import", "radio").click() - self.forward(newvm) - newvm.find("import-entry").set_text("/dev/default-pool/testvol1.img") - newvm.find("oslist-entry").set_text("fedora30") - popover = newvm.find("oslist-popover") - popover.find("include-eol").click() - popover.find_fuzzy("Fedora 30").click() - self.forward(newvm) - self.forward(newvm) - - # Select customize wizard, we will use this VM to - # hit some PPC64 code paths elsewhere - newvm.find_fuzzy("Customize", "check").click() - newvm.find_fuzzy("Finish", "button").click() - vmname = "fedora30" - details = self.app.root.find_fuzzy("%s on" % vmname, "frame") - - # Change to UEFI - details.combo_check_default("Chipset:", "Q35") - details.combo_check_default("Firmware:", "BIOS") - details.combo_select("Firmware:", ".*x86_64.*") - details.find("config-apply").click() - - # Finish - details.find_fuzzy("Begin Installation", "button").click() - lib.utils.check(lambda: details.dead) - self.app.root.find_fuzzy("%s on" % vmname, "frame") - - def testNewPPC64(self): - """ - New PPC64 VM to test architecture selection - """ - self.app.uri = tests.utils.URIs.kvm - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Architecture options", "toggle").click() - newvm.combo_select("Architecture", ".*ppc64.*") - newvm.combo_check_default("Machine Type", ".*pseries.*") - - newvm.find_fuzzy("Manual", "radio").click() - self.forward(newvm) - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(newvm) - self.forward(newvm) - # Disable storage, we add some via customize - newvm.find_fuzzy("Enable storage", "check box").click() - self.forward(newvm) - - # Select customize wizard, we will use this VM to - # hit some PPC64 code paths elsewhere - newvm.find_fuzzy("Customize", "check").click() - newvm.find_fuzzy("Finish", "button").click() - details = self.app.root.find_fuzzy("vm-ppc64 on", "frame") - - tab = details.find("overview-tab") - tab.combo_check_default("machine-combo", "pseries") - tab.combo_select("machine-combo", "pseries-2.1") - appl = details.find("config-apply") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Add a TPM SPAPR device - details.find("add-hardware", "push button").click() - addhw = self.app.root.find("Add New Virtual Hardware", "frame") - addhw.find("TPM", "table cell").click() - tab = addhw.find("tpm-tab", None) - lib.utils.check(lambda: tab.showing) - addhw.find("Finish", "push button").click() - lib.utils.check(lambda: not addhw.active) - lib.utils.check(lambda: details.active) - - # Add a SCSI disk which also adds virtio-scsi controller - details.find("add-hardware", "push button").click() - addhw = self.app.root.find("Add New Virtual Hardware", "frame") - addhw.find("Storage", "table cell").click() - tab = addhw.find("storage-tab", None) - lib.utils.check(lambda: tab.showing) - tab.combo_select("Bus type:", "SCSI") - addhw.find("Finish", "push button").click() - lib.utils.check(lambda: not addhw.active) - lib.utils.check(lambda: details.active) - - # Finish - details.find_fuzzy("Begin Installation", "button").click() - lib.utils.check(lambda: details.dead) - self.app.root.find_fuzzy("vm-ppc64 on", "frame") - - def testNewVMAArch64UEFI(self): - """ - Test aarch64 UEFI usage - """ - self.app.uri = tests.utils.URIs.kvm_aarch64 - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Local install media", "radio").click() - self.forward(newvm) - - newvm.find_fuzzy("Automatically detect", "check").click() - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - newvm.find("media-entry").set_text("/dev/default-pool/testvol1.img") - self.forward(newvm) - self.forward(newvm) - newvm.find_fuzzy("Enable storage", "check box").click() - self.forward(newvm) - newvm.find_fuzzy("Finish", "button").click() - - self.app.root.find_fuzzy("vm1 on", "frame") - lib.utils.check(lambda: not newvm.showing) - - def testNewVMArmKernel(self): - """ - New arm VM that requires kernel/initrd/dtb - """ - self.app.uri = tests.utils.URIs.kvm_armv7l_nodomcaps - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Architecture options", "toggle").click_expander() - newvm.find_fuzzy("Virt Type", "combo").click() - KVM = newvm.find_fuzzy("KVM", "menu item") - TCG = newvm.find_fuzzy("TCG", "menu item") - lib.utils.check(lambda: KVM.focused) - lib.utils.check(lambda: TCG.showing) - self.app.rawinput.pressKey("Esc") - - # Validate some initial defaults - local = newvm.find_fuzzy("Local", "radio") - lib.utils.check(lambda: not local.sensitive) - newvm.find_fuzzy("Machine Type", "combo").click() - self.app.sleep(.2) - newvm.find_fuzzy("canon", "menu item").click() - newvm.find_fuzzy("Machine Type", "combo").click() - self.app.sleep(.2) - newvm.find("virt", "menu item").click() - self.app.sleep(.5) - importradio = newvm.find("Import", "radio") - importradio.click() - lib.utils.check(lambda: importradio.checked) - self.forward(newvm) - - newvm.find("import-entry").set_text("/dev/default-pool/default-vol") - # Make sure the info box shows up - newvm.find("Kernel/initrd settings can be configured") - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(newvm, check=False) - - # Disk collision box pops up, hit ok - self.app.click_alert_button("in use", "Yes") - - self.forward(newvm) - newvm.find_fuzzy("Finish", "button").click() - - lib.utils.check(lambda: not newvm.showing) - self.app.root.find_fuzzy("vm1 on", "frame") - - - def testNewVMContainerApp(self): - """ - Simple LXC app install - """ - self.app.uri = tests.utils.URIs.lxc - - newvm = self._open_create_wizard() - newvm.find_fuzzy("Application", "radio").click() - self.forward(newvm) - - # Set custom init - apptext = newvm.find_fuzzy(None, "text", "application path") - apptext.set_text("") - self.forward(newvm, check=False) - self.app.click_alert_button("path is required", "OK") - newvm.find("install-app-browse").click() - self.app.select_storagebrowser_volume("default-pool", "aaa-unused.qcow2") - lib.utils.check(lambda: "aaa-unused.qcow2" in apptext.text) - - self.forward(newvm) - self.forward(newvm) - # Trigger back, to ensure disk page skipping works - self.back(newvm) - self.back(newvm) - self.forward(newvm) - self.forward(newvm) - - # Select customize wizard, we will use this VM to hit specific - # code paths - newvm.find_fuzzy("Customize", "check").click() - newvm.find_fuzzy("Finish", "button").click() - vmname = "container1" - details = self.app.root.find_fuzzy("%s on" % vmname, "frame") - - # Tweak init values - details.find("Boot Options", "table cell").click() - tab = details.find("boot-tab") - tab.find("Init path:", "text").set_text("") - tab.find("Init args:", "text").set_text("some args") - appl = details.find("config-apply") - appl.click() - self.app.click_alert_button("init path must be specified", "OK") - lib.utils.check(lambda: appl.sensitive) - tab.find("Init path:", "text").set_text("/some/path") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Check that addhw container options are disabled - details.find("add-hardware", "push button").click() - addhw = self.app.root.find("Add New Virtual Hardware", "frame") - addhw.find("PCI Host Device", "table cell").click() - # Ensure the error label is showing - label = addhw.find("Not supported for containers") - label.check_onscreen() - addhw.find("Cancel", "push button").click() - lib.utils.check(lambda: not addhw.active) - lib.utils.check(lambda: details.active) - - # Finish - details.find_fuzzy("Begin Installation", "button").click() - lib.utils.check(lambda: not newvm.showing) - self.app.root.find_fuzzy("%s on" % vmname, "frame") - - def testNewVMCustomizeCancel(self): - """ - Test cancelling out of the customize wizard - """ - newvm = self._open_create_wizard() - newvm.find_fuzzy("Manual", "radio").click() - self.forward(newvm) - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(newvm) - self.forward(newvm) - self.forward(newvm) - - newvm.find_fuzzy("Customize", "check").click() - newvm.find_fuzzy("Finish", "button").click() - vmname = "vm1" - details = self.app.root.find_fuzzy("%s on" % vmname, "frame") - - details.find("Cancel Installation", "push button").click() - self.app.click_alert_button("abort the installation", "No") - lib.utils.check(lambda: details.active) - details.find("Cancel Installation", "push button").click() - self.app.click_alert_button("abort the installation", "Yes") - lib.utils.check(lambda: not details.active) - lib.utils.check(lambda: not newvm.active) - - def testNewVMCustomizeMisc(self): - """ - Some specific customize logic paths - """ - newvm = self._open_create_wizard() - newvm.find_fuzzy("Manual", "radio").click() - self.forward(newvm) - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(newvm) - self.forward(newvm) - self.forward(newvm) - - newvm.find_fuzzy("Customize", "check").click() - newvm.find_fuzzy("Finish", "button").click() - vmname = "vm1" - details = self.app.root.find_fuzzy("%s on" % vmname, "frame") - - # Test name change - tab = details.find("overview-tab") - nametext = tab.find("Name:", "text") - nametext.set_text("foonewname") - details.find("config-apply").click() - self.app.root.find_fuzzy("foonewname", "frame") - - # Trigger XML failure to hit some codepaths - nametext.set_text("") - details.find("Begin Installation").click() - self.app.click_alert_button("unapplied changes", "Yes") - self.app.click_alert_button("name must be specified", "Close") - lib.utils.check(lambda: details.showing) - - # Discard XML change and continue with install - details.find("Begin Installation").click() - self.app.click_alert_button("unapplied changes", "No") - lib.utils.check(lambda: not details.showing) - lib.utils.check(lambda: not newvm.showing) - self.app.root.find_fuzzy("foonewname on", "frame") - - - def testNewVMContainerTree(self): - """ - Simple LXC tree install - """ - self.app.uri = tests.utils.URIs.lxc - - newvm = self._open_create_wizard() - newvm.find_fuzzy("Operating system", "radio").click() - self.forward(newvm) - - # Set directory path - dirtext = newvm.find_fuzzy(None, "text", "root directory") - dirtext.set_text("") - self.forward(newvm, check=False) - self.app.click_alert_button("path is required", "OK") - - newvm.find("install-oscontainer-browse").click() - self.app.select_storagebrowser_volume("default-pool", "dir-vol") - lib.utils.check(lambda: "dir-vol" in dirtext.text) - - self.forward(newvm) - self.forward(newvm) - newvm.find_fuzzy("Finish", "button").click() - - lib.utils.check(lambda: not newvm.showing) - self.app.root.find_fuzzy("container1 on", "frame") - - - def testNewVMContainerVZ(self): - """ - Virtuozzo container install - """ - self.app.uri = tests.utils.URIs.vz - - newvm = self._open_create_wizard() - newvm.find_fuzzy("Container", "radio").click() - newvm.find_fuzzy("Virtual machine", "radio").click() - newvm.find_fuzzy("Container", "radio").click() - self.forward(newvm) - - # Set directory path - templatetext = newvm.find_fuzzy(None, "text", "container template") - templatetext.set_text("") - self.forward(newvm, check=False) - self.app.click_alert_button("template name is required", "OK") - templatetext.set_text("centos-6-x86_64") - self.forward(newvm) - self.forward(newvm) - newvm.find_fuzzy("Finish", "button").click() - - self.app.root.find_fuzzy("container1 on", "frame") - lib.utils.check(lambda: not newvm.showing) - - - def testNewVMContainerBootstrap(self): - self.app.uri = tests.utils.URIs.lxc - try: - import virtBootstrap # pylint: disable=unused-import - except ImportError: - self.skipTest("virtBootstrap not installed") - - newvm = self._open_create_wizard() - newvm.find_fuzzy("Operating system", "radio").click() - self.forward(newvm) - - # Set directory path - import tempfile - tmpdir = tempfile.TemporaryDirectory() - newvm.find_fuzzy("Create OS directory", "check box").click() - - uritext = newvm.find("install-oscontainer-source-uri") - uritext.text = "" - self.forward(newvm, check=False) - self.app.click_alert_button("Source URL is required", "OK") - uritext.text = "docker://alpine" - - rootdir = newvm.find_fuzzy(None, "text", "root directory") - lib.utils.check(lambda: ".local/share/libvirt" in rootdir.text) - rootdir.set_text("/dev/null") - self.forward(newvm, check=False) - self.app.click_alert_button("not directory", "OK") - rootdir.set_text("/root") - self.forward(newvm, check=False) - self.app.click_alert_button("No write permissions", "OK") - rootdir.set_text("/tmp") - self.forward(newvm, check=False) - self.app.click_alert_button("directory is not empty", "No") - rootdir.set_text(tmpdir.name) - newvm.find("install-oscontainer-root-passwd").set_text("foobar") - # Invalid credentials to trigger failure - newvm.find("Credentials", "toggle button").click_expander() - newvm.find("bootstrap-registry-user").set_text("foo") - self.forward(newvm, check=None) - self.app.click_alert_button("Please specify password", "OK") - newvm.find("bootstrap-registry-password").set_text("bar") - - self.forward(newvm) - self.forward(newvm) - newvm.find_fuzzy("Finish", "button").click() - self.app.click_alert_button("virt-bootstrap did not complete", "Close") - self.back(newvm) - self.back(newvm) - newvm.find("bootstrap-registry-user").set_text("") - newvm.find("bootstrap-registry-password").set_text("") - - self.forward(newvm) - self.forward(newvm) - newvm.find_fuzzy("Finish", "button").click() - prog = self.app.root.find("Creating Virtual Machine", "frame") - lib.utils.check(lambda: not prog.showing, timeout=30) - - lib.utils.check(lambda: not newvm.showing) - self.app.root.find_fuzzy("container1 on", "frame") - - - def testNewVMXenPV(self): - """ - Test the create wizard with a fake xen PV install - """ - self.app.uri = tests.utils.URIs.xen - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Architecture options", "toggle").click() - newvm.combo_select("Xen Type", ".*paravirt.*") - - newvm.find_fuzzy("Import", "radio").click() - self.forward(newvm) - newvm.find("import-entry").set_text("/dev/default-pool/testvol1.img") - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(newvm) - self.forward(newvm) - newvm.find_fuzzy("Finish", "button").click() - - self.app.root.find_fuzzy("vm1 on", "frame") - lib.utils.check(lambda: not newvm.showing) - - - def testNewVMInstallFail(self): - def dofail(): - _newvm = self._open_create_wizard() - _newvm.find_fuzzy("Manual", "radio").click() - self.forward(_newvm) - _newvm.find("oslist-entry").set_text("generic") - _newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(_newvm) - self.forward(_newvm) - self.forward(_newvm) - - # '/' in name will trigger libvirt error - _newvm.find_fuzzy("Name", "text").set_text("test/bad") - _newvm.find_fuzzy("Finish", "button").click() - self.app.click_alert_button("Unable to complete install", "Close") - return _newvm - - newvm = dofail() - pathlabel = newvm.find(".*test/bad.qcow2") - generatedpath = pathlabel.text - # Changing VM name should not generate a new path - newvm.find_fuzzy("Name", "text").set_text("test/badfoo") - lib.utils.check(lambda: pathlabel.text == generatedpath) - newvm.find_fuzzy("Finish", "button").click() - self.app.click_alert_button("Unable to complete install", "Close") - # Closing dialog should trigger storage cleanup path - newvm.find_fuzzy("Cancel", "button").click() - lib.utils.check(lambda: not newvm.visible) - - # Run again - newvm = dofail() - self.back(newvm) - newvm.find_fuzzy("Select or create", "radio").click() - - newvm.find("storage-entry").set_text("/dev/default-pool/somenewvol1") - self.forward(newvm) - newvm.find_fuzzy("Name", "text").set_text("test-foo") - newvm.find_fuzzy("Finish", "button").click() - - self.app.root.find_fuzzy("test-foo on", "frame") - lib.utils.check(lambda: not newvm.showing) - - - def testNewVMCustomizeXMLEdit(self): - """ - Test new VM with raw XML editing via customize wizard - """ - self.app.open(xmleditor_enabled=True) - newvm = self._open_create_wizard() - - # Create a custom named VM, using CDROM media, and default storage - vmname = "fooxmleditvm" - newvm.find_fuzzy("Local install media", "radio").click() - newvm.find_fuzzy("Forward", "button").click() - nonexistpath = "/dev/foovmm-idontexist" - existpath = "/dev/default-pool/testvol1.img" - newvm.find("media-entry").set_text(nonexistpath) - lib.utils.check( - lambda: newvm.find("oslist-entry").text == "None detected") - newvm.find_fuzzy("Automatically detect", "check").click() - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(newvm, check=False) - self.app.click_alert_button("Error setting installer", "OK") - newvm.find("media-entry").set_text(existpath) - self.forward(newvm) - self.forward(newvm) - self.forward(newvm) - newvm.find_fuzzy("Customize", "check").click() - newvm.find_fuzzy("Name", "text").set_text(vmname) - newvm.find_fuzzy("Finish", "button").click() - win = self.app.root.find_fuzzy("%s on" % vmname, "frame") - xmleditor = win.find("XML editor") - finish = win.find("config-apply") - - # Change a device setting with the XML editor - win.find_fuzzy("IDE Disk 1", "table cell").click() - tab = win.find("disk-tab") - win.find("XML", "page tab").click() - # Change the disk path via the XML editor - fname = vmname + ".qcow2" - lib.utils.check(lambda: fname in xmleditor.text) - newx = xmleditor.text.replace(fname, "default-vol") - xmleditor.set_text(newx) - appl = win.find("config-apply") - # This is kindof a bug, changing path in XML editor in Customize - # doesn't take effect for storage with creation parameters, but - # it's a pain to fix. - appl.click() - lib.utils.check(lambda: not appl.sensitive) - lib.utils.check(lambda: vmname in xmleditor.text) - - # Change a VM setting and verify it - win.find_fuzzy("Boot", "table cell").click() - tab = win.find("boot-tab") - bootmenu = tab.find("Enable boot menu", "check box") - lib.utils.check(lambda: not bootmenu.checked) - win.find("XML", "page tab").click() - newtext = xmleditor.text.replace( - "", "") - xmleditor.set_text(newtext) - finish.click() - win.find("Details", "page tab").click() - lib.utils.check(lambda: bootmenu.checked) - - # Change a device setting with the XML editor - win.find_fuzzy("NIC", "table cell").click() - tab = win.find("network-tab") - win.find("XML", "page tab").click() - newbrname = "BRFAKE" - newx = xmleditor.text.replace("network", "bridge") - newx = newx.replace('bridge="default"', "bridge='%s'" % newbrname) - xmleditor.set_text(newx) - finish.click() - - # Finish install. - win.find_fuzzy("Begin Installation", "button").click() - lib.utils.check(lambda: win.dead) - win = self.app.root.find_fuzzy("%s on" % vmname, "frame") - win.find("Details", "radio button").click() - - # Verify VM change stuck - win.find_fuzzy("Boot", "table cell").click() - tab = win.find("boot-tab") - bootmenu = tab.find("Enable boot menu", "check box") - lib.utils.check(lambda: bootmenu.checked) - - # Verify device change stuck - win.find_fuzzy("NIC", "table cell").click() - tab = win.find("network-tab") - devname = tab.find("Device name:", "text") - lib.utils.check(lambda: devname.text == newbrname) - - # Verify install media is handled correctly after XML customize - win.find_fuzzy("IDE CDROM 1", "table cell").click() - tab = win.find("disk-tab") - mediaent = tab.find("media-entry") - lib.utils.check(lambda: mediaent.text == existpath) - win.find("Shut Down", "push button").click() - run = win.find("Run", "push button") - lib.utils.check(lambda: run.sensitive) - lib.utils.check(lambda: mediaent.text == "") - - # Verify default disk storage was actually created. This has some - # special handling in domain.py - tab.find("Browse", "push button").click() - browser = self.app.root.find("vmm-storage-browser") - browser.find("%s.qcow2" % vmname, "table cell") - - def testNewVMRemote(self): - """ - Hit some is_remote code paths - """ - self.app.uri = tests.utils.URIs.test_remote - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Import", "radio").click() - self.forward(newvm) - importtext = newvm.find("import-entry") - - # Click forward, hitting missing Import path error - self.forward(newvm, check=False) - self.app.click_alert_button("import is required", "OK") - - # Click forward, but Import path doesn't exist - importtext.set_text("/dev/default-pool/idontexist") - self.forward(newvm, check=False) - self.app.click_alert_button("import path must point", "OK") - importtext.set_text("/dev/default-pool/default-vol") - - # Click forward, hitting missing OS error - self.forward(newvm, check=False) - self.app.click_alert_button("select an OS", "OK") - - # Set OS - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - - # Click forward, but Import path is in use, and exit - self.forward(newvm, check=False) - self.app.click_alert_button("in use", "No") - - # storagebrowser bits - newvm.find("install-import-browse").click() - browsewin = self.app.root.find("vmm-storage-browser") - # Insensitive for remote connection - browselocal = browsewin.find("Browse Local") - lib.utils.check(lambda: browselocal.sensitive is False) - # Close the browser and reopen - browsewin.find("Cancel").click() - lib.utils.check(lambda: not browsewin.active) - # Reopen, select storage - newvm.find("install-import-browse").click() - self.app.select_storagebrowser_volume("default-pool", "bochs-vol") - lib.utils.check( - lambda: importtext.text == "/dev/default-pool/bochs-vol") - - self.forward(newvm) - self.forward(newvm) - - newvm.find_fuzzy("Finish", "button").click() - self.app.root.find_fuzzy("vm1 on", "frame") - lib.utils.check(lambda: not newvm.showing) - - def testNewVMSession(self): - """ - Test with fake qemu session - """ - self.app.uri = tests.utils.URIs.kvm_session - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Import", "radio").click() - self.forward(newvm) - newvm.find("import-entry").set_text("/dev/default-pool/testvol1.img") - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(newvm) - self.forward(newvm) - newvm.combo_check_default("net-source", "Usermode") - - newvm.find_fuzzy("Finish", "button").click() - self.app.root.find_fuzzy("vm1 on", "frame") - lib.utils.check(lambda: not newvm.showing) - - def testNewVMEmptyConn(self): - """ - Test with an empty connection - """ - self.app.uri = tests.utils.URIs.test_empty - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Import", "radio").click() - self.forward(newvm) - newvm.find("import-entry").set_text(__file__) - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(newvm) - self.forward(newvm) - newvm.combo_check_default("net-source", "Bridge") - warnlabel = newvm.find_fuzzy("suitable default network", "label") - warnlabel.check_onscreen() - newvm.find("Device name:", "text").set_text("foobr0") - - # Select customize wizard, we will use this VM to hit specific - # code paths - newvm.find_fuzzy("Customize", "check").click() - newvm.find_fuzzy("Finish", "button").click() - vmname = "vm1" - details = self.app.root.find_fuzzy("%s on" % vmname, "frame") - - # Check that addhw hostdev drop down is empty - details.find("add-hardware", "push button").click() - addhw = self.app.root.find("Add New Virtual Hardware", "frame") - addhw.find("USB Host Device", "table cell").click() - tab = addhw.find("host-tab", None) - lib.utils.check(lambda: tab.showing) - cell = tab.find("No Devices", "table cell") - lib.utils.check(lambda: cell.selected) - addhw.find("Cancel", "push button").click() - lib.utils.check(lambda: not addhw.active) - lib.utils.check(lambda: details.active) - - # Finish - details.find_fuzzy("Begin Installation", "button").click() - lib.utils.check(lambda: details.dead) - self.app.root.find_fuzzy("%s on" % vmname, "frame") - - def testNewVMInactiveNetwork(self): - """ - Test with an inactive 'default' network - """ - self.app.uri = tests.utils.URIs.test_default - hostwin = self.app.open_host_window("Virtual Networks", - conn_label="test default") - cell = hostwin.find("default", "table cell") - cell.click() - hostwin.find("net-stop").click() - hostwin.keyCombo("w") - - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Import", "radio").click() - self.forward(newvm) - newvm.find("import-entry").set_text(__file__) - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(newvm) - self.forward(newvm) - - newvm.find_fuzzy("Finish", "button").click() - self.app.click_alert_button("start the network", "Yes") - lib.utils.check(lambda: not newvm.showing) - - @unittest.mock.patch.dict('os.environ', {"VIRTINST_TEST_SUITE": "1"}) - def testNewVMDefaultBridge(self): - """ - We actually set the unittest env variable here, which - sets a fake bridge in interface.py - """ - self.app.uri = tests.utils.URIs.test_empty - newvm = self._open_create_wizard() - - newvm.find_fuzzy("Import", "radio").click() - self.forward(newvm) - newvm.find("import-entry").set_text(__file__) - newvm.find("oslist-entry").set_text("generic") - newvm.find("oslist-popover").find_fuzzy("generic").click() - self.forward(newvm) - self.forward(newvm) - newvm.find("Network selection", "toggle button").click_expander() - newvm.combo_check_default("net-source", "Bridge") - devname = newvm.find("Device name:", "text") - lib.utils.check(lambda: devname.text == "testsuitebr0") - - newvm.find_fuzzy("Finish", "button").click() - self.app.root.find_fuzzy("vm1 on", "frame") - lib.utils.check(lambda: not newvm.showing) + manager = app.topwin + + def _add_conn(uri): + return app.manager_createconn(uri) + + # Check the dialog shows 'no connection' error + app.manager_conn_disconnect("test testdriver.xml") + newvm = _open_newvm(app) + newvm.find_fuzzy("No active connection to install on") + newvm.keyCombo("F4") + lib.utils.check(lambda: manager.active) + + # Check the xen PV only startup warning + def _capsopt(fname): + capsdir = tests.utils.DATADIR + "/capabilities/" + return ",caps=" + capsdir + fname + + # Test empty qemu connection + _add_conn(tests.utils.URIs.kvm + _capsopt("test-empty.xml")) + newvm = _open_newvm(app) + newvm.find(".*No hypervisor options were found.*KVM kernel modules.*") + newvm.click() + newvm.click_title() + newvm.keyCombo("F4") + app.manager_conn_disconnect("QEMU/KVM") + + _add_conn(tests.utils.URIs.kvm_session + + _capsopt("test-qemu-no-kvm.xml")) + newvm = _open_newvm(app) + newvm.find(".*KVM is not available.*") + newvm.click() + newvm.click_title() + newvm.keyCombo("F4") + + _add_conn(tests.utils.URIs.lxc) + _add_conn(tests.utils.URIs.test_full) + _add_conn(tests.utils.URIs.test_default) + + # Open the new VM wizard, select a connection + newvm = _open_newvm(app) + newvm.combo_select("create-conn", ".*testdriver.xml.*") + _forward(newvm) + + # Verify media-combo contents for testdriver.xml + cdrom = newvm.find("media-combo") + entry = newvm.find("media-entry") + cdrom.click_combo_entry() + cdrom.find_fuzzy(r"\(/dev/sr1\)") + entry.click() + # Launch this so we can verify storage browser is reset too + newvm.find_fuzzy("install-iso-browse", "button").click() + app.select_storagebrowser_volume("default-pool", "iso-vol") + newvm.find_fuzzy("Automatically detect", "check").click() + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(newvm) + + # Back up, select test:///default, verify media-combo is now empty + newvm.click_title() + newvm.keyCombo("F4") + newvm = _open_newvm(app) + newvm.combo_select("create-conn", ".*test default.*") + _forward(newvm) + cdrom.click_combo_entry() + lib.utils.check(lambda: "/dev/sr1" not in cdrom.fmt_nodes()) + newvm.find_fuzzy("install-iso-browse", "button").click() + browsewin = app.root.find("vmm-storage-browser") + lib.utils.check(lambda: "disk-pool" not in browsewin.fmt_nodes()) + + +def testNewVMManualDefault(app): + """ + Click through the New VM wizard with default values + manual, then + delete the VM + """ + newvm = _open_newvm(app) + + newvm.find_fuzzy("Manual", "radio").click() + _forward(newvm) + osentry = newvm.find("oslist-entry") + lib.utils.check(lambda: not osentry.text) + + # Make sure we throw an error if no OS selected + _forward(newvm, check=False) + app.click_alert_button("You must select", "OK") + + # Test activating the osentry to grab the popover selection + osentry.click() + osentry.typeText("generic") + newvm.find("oslist-popover") + osentry.click() + app.rawinput.pressKey("Enter") + lib.utils.check(lambda: osentry.text == "Generic OS") + + # Verify back+forward still keeps Generic selected + app.sleep(.5) + _back(newvm) + app.sleep(.5) + _forward(newvm) + app.sleep(.5) + lib.utils.check(lambda: "Generic" in osentry.text) + osentry.check_onscreen() + + # The sleeps shouldn't be required, but this test continues to be + # flakey, so this is an attempt to fix it. + _forward(newvm) + app.sleep(.5) + _forward(newvm) + app.sleep(.5) + _forward(newvm) + app.sleep(.5) + + + # Empty triggers a specific codepath + newvm.find_fuzzy("Name", "text").set_text("") + # Name collision failure + newvm.find_fuzzy("Name", "text").set_text("test-many-devices") + newvm.find_fuzzy("Finish", "button").click() + app.click_alert_button("in use", "OK") + newvm.find_fuzzy("Name", "text").set_text("vm1") + newvm.find_fuzzy("Finish", "button").click() + + # Delete it from the VM window + vmwindow = app.find_details_window("vm1") + vmwindow.find("Virtual Machine", "menu").click() + vmwindow.find("Delete", "menu item").click() + + delete = app.find_window("Delete") + delete.find_fuzzy("Delete", "button").click() + app.click_alert_button("Are you sure", "Yes") + + # Verify delete dialog and VM dialog are now gone + lib.utils.check(lambda: vmwindow.showing is False) + + +def testNewVMStorage(app): + """ + Test some storage specific paths + """ + newvm = _open_newvm(app) + + newvm.find_fuzzy("Manual", "radio").click() + _forward(newvm) + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(newvm) + _forward(newvm) + + # Trigger size validation failure + sizetext = newvm.find(None, "spin button", "GiB") + sizetext.set_text("10000000") + _forward(newvm, check=False) + app.click_alert_button("Storage parameter error", "OK") + sizetext.set_text("1") + + # Use the storage browser to select a local file + storagetext = newvm.find("storage-entry") + newvm.find_fuzzy("Select or create", "radio").click() + newvm.find("storage-browse").click() + browse = app.root.find("vmm-storage-browser") + browse.find("Browse Local", "push button").click() + chooser = app.root.find( + "Locate existing storage", "file chooser") + fname = "COPYING" + chooser.find(fname, "table cell").click() + chooser.find("Open", "push button").click() + lib.utils.check(lambda: newvm.active) + lib.utils.check(lambda: "COPYING" in storagetext.text) + + # Start the install + _forward(newvm) + newvm.find("Finish", "push button").click() + app.find_details_window("vm1") + lib.utils.check(lambda: not newvm.showing) + + + +def testNewVMCDROMRegular(app): + """ + Create a new CDROM VM, choosing distro win8, and do some basic + 'Customize before install' before exiting + """ + newvm = _open_newvm(app) + + newvm.find_fuzzy("Local install media", "radio").click() + _forward(newvm) + + # check prepopulated cdrom media + combo = newvm.find("media-combo") + combo.click_combo_entry() + combo.find(r"No media detected \(/dev/sr1\)") + combo.find(r"Fedora12_media \(/dev/sr0\)").click() + + # Catch validation error + entry = newvm.find("media-entry") + entry.click() + entry.set_text("") + _forward(newvm, check=False) + app.click_alert_button("media selection is required", "OK") + + # test entry activation too + entry.click() + entry.set_text("/dev/sr0") + app.rawinput.pressKey("Enter") + + # Select a fake iso + newvm.find_fuzzy("install-iso-browse", "button").click() + app.select_storagebrowser_volume("default-pool", "iso-vol") + + osentry = newvm.find("oslist-entry") + lib.utils.check(lambda: osentry.text == "None detected") + + # Change distro to win8 + newvm.find_fuzzy("Automatically detect", "check").click() + osentry.click() + osentry.set_text("windows 8") + popover = newvm.find("oslist-popover") + popover.check_onscreen() + # Verify Escape resets the text entry + app.rawinput.pressKey("Escape") + popover.check_not_onscreen() + lib.utils.check(lambda: osentry.text == "") + # Re-enter text + osentry.set_text("windows 8") + popover.check_onscreen() + popover.find_fuzzy("include-eol").click() + popover.find_fuzzy(r"\(win8\)").click() + popover.check_not_onscreen() + foundtext = osentry.text + # Start typing again, and exit, make sure it resets to previous entry + osentry.click() + osentry.set_text("foo") + popover.check_onscreen() + app.rawinput.pressKey("Escape") + popover.check_not_onscreen() + lib.utils.check(lambda: osentry.text == foundtext) + _forward(newvm) + + # Verify that CPU values are non-default + cpus = newvm.find("cpus", "spin button") + lib.utils.check(lambda: int(cpus.text) > 1, timeout=5) + _forward(newvm) + _forward(newvm) + + # Select customize wizard + newvm.find_fuzzy("Customize", "check").click() + newvm.find_fuzzy("Finish", "button").click() + + # Verify CDROM media is inserted + vmwindow = app.find_details_window("win8") + vmwindow.find_fuzzy("IDE CDROM", "table cell").click() + mediaent = vmwindow.find("media-entry") + lib.utils.check(lambda: "iso-vol" in mediaent.text) + + # Change boot autostart + vmwindow.find_fuzzy("Boot", "table cell").click() + vmwindow.find_fuzzy("Start virtual machine", "check").click() + vmwindow.find_fuzzy("config-apply").click() + + # Change to 'copy host CPU' + vmwindow.find_fuzzy("CPUs", "table cell").click() + vmwindow.find_fuzzy("Copy host", "check").click() + vmwindow.find_fuzzy("config-apply").click() + + # Add a default disk + vmwindow.find("add-hardware", "push button").click() + addhw = app.find_window("Add New Virtual Hardware") + addhw.find("Finish", "push button").click() + lib.utils.check(lambda: vmwindow.active) + + # Select the new disk, change the bus to USB + vmwindow.find_fuzzy("IDE Disk 2", "table cell").click() + appl = vmwindow.find("config-apply", "push button") + hwlist = vmwindow.find("hw-list") + tab = vmwindow.find("disk-tab") + tab.find("Disk bus:", "text").set_text("usb") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + # Device is now 'USB Disk 1' + c = hwlist.find("USB Disk 1", "table cell") + lib.utils.check(lambda: c.state_selected) + tab.find("Advanced options", "toggle button").click_expander() + tab.find("Removable:", "check box").click() + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Change NIC mac + vmwindow.find_fuzzy("NIC", "table cell").click() + tab = vmwindow.find("network-tab") + tab.find("mac-entry", "text").set_text("00:11:00:11:00:11") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Start the install, close via the VM window + vmwindow.find_fuzzy("Begin Installation", "button").click() + lib.utils.check(lambda: newvm.showing is False) + vmwindow = app.find_details_window("win8") + vmwindow.find_fuzzy("File", "menu").click() + vmwindow.find_fuzzy("Quit", "menu item").click() + lib.utils.check(lambda: app.is_running()) + + +def testNewVMCDROMDetect(app): + """ + CDROM with detection + """ + cdrom = tests.utils.DATADIR + "/fakemedia/fake-win7.iso" + newvm = _open_newvm(app) + newvm.find_fuzzy("Local install media", "radio").click() + _forward(newvm) + newvm.find("media-entry").click() + newvm.find("media-entry").set_text(cdrom) + # Use forward to trigger detection + _forward(newvm) + _forward(newvm) + _forward(newvm) + newvm.find("Finish", "push button").click() + app.find_details_window("win7") + lib.utils.check(lambda: not newvm.showing) + + + +def testNewVMURL(app): + """ + New VM with URL and distro detection, plus having fun with + the storage browser and network selection. + """ + app.uri = tests.utils.URIs.kvm + newvm = _open_newvm(app) + + newvm.find_fuzzy("Network Install", "radio").click() + _forward(newvm) + osentry = newvm.find("oslist-entry") + lib.utils.check(lambda: osentry.text.startswith("Waiting")) + + newvm.find("install-url-entry").set_text("") + _forward(newvm, check=False) + app.click_alert_button("tree is required", "OK") + + url = "https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/10/Fedora/x86_64/os/" + oslabel = "Fedora 10" + newvm.find("install-url-entry").set_text(url) + newvm.find("install-url-entry").click() + app.rawinput.pressKey("Enter") + newvm.find("install-urlopts-expander").click_expander() + newvm.find("install-urlopts-entry").set_text("foo=bar") + + lib.utils.check(lambda: osentry.text == oslabel, timeout=10) + + # Move forward, then back, ensure OS stays selected + _forward(newvm) + _back(newvm) + lib.utils.check(lambda: osentry.text == oslabel) + + # Disable autodetect, make sure OS still selected + newvm.find_fuzzy("Automatically detect", "check").click() + lib.utils.check(lambda: osentry.text == oslabel) + _forward(newvm) + _back(newvm) + + # Ensure the EOL field was selected + osentry.click() + app.rawinput.pressKey("Down") + popover = newvm.find("oslist-popover") + lib.utils.check(lambda: popover.showing) + includeeol = newvm.find("include-eol", "check") + lib.utils.check(lambda: includeeol.isChecked) + + # Re-enable autodetect, check for detecting text + newvm.find_fuzzy("Automatically detect", "check").click() + lib.utils.check(lambda: not popover.showing) + lib.utils.check(lambda: "Detecting" in osentry.text) + lib.utils.check(lambda: osentry.text == oslabel, timeout=10) + + # Progress the install + _forward(newvm) + _forward(newvm) + _forward(newvm) + newvm.find_fuzzy("Finish", "button").click() + + progress = app.find_window("Creating Virtual Machine") + lib.utils.check(lambda: not progress.showing, timeout=120) + + details = app.find_details_window("fedora10") + lib.utils.check(lambda: not newvm.showing) + + # Re-run the newvm wizard, check that URL was remembered + details.keyCombo("F4") + newvm = _open_newvm(app) + newvm.find_fuzzy("Network Install", "radio").click() + _forward(newvm) + urlcombo = newvm.find("install-url-combo") + lib.utils.check(lambda: urlcombo.showing) + lib.utils.check(lambda: url in urlcombo.fmt_nodes()) + + +def testNewKVMQ35Tweaks(app): + """ + New VM that should default to Q35, but tweak things a bunch + """ + app.uri = tests.utils.URIs.kvm + newvm = _open_newvm(app) + + newvm.find_fuzzy("Import", "radio").click() + _forward(newvm) + newvm.find("import-entry").set_text("/dev/default-pool/testvol1.img") + newvm.find("oslist-entry").set_text("fedora30") + popover = newvm.find("oslist-popover") + popover.find("include-eol").click() + popover.find_fuzzy("Fedora 30").click() + _forward(newvm) + _forward(newvm) + + # Select customize wizard, we will use this VM to + # hit some code paths elsewhere + newvm.find_fuzzy("Customize", "check").click() + newvm.find_fuzzy("Finish", "button").click() + vmname = "fedora30" + details = app.find_details_window(vmname) + appl = details.find("config-apply") + + # Tweak some Overview settings + details.combo_check_default("Chipset:", "Q35") + details.combo_check_default("Firmware:", "BIOS") + + # Switch i440FX and back + details.combo_select("Chipset:", "i440FX") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + details.combo_select("Chipset:", "Q35") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + # Switch to UEFI, back to BIOS, back to UEFI + details.combo_select("Firmware:", ".*x86_64.*") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + # Switch back to BIOS + details.combo_select("Firmware:", "BIOS") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + # Switch back to UEFI + details.combo_select("Firmware:", ".*x86_64.*") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Add another network device + details.find("add-hardware", "push button").click() + addhw = app.find_window("Add New Virtual Hardware") + addhw.find("Network", "table cell").click() + tab = addhw.find("network-tab", None) + lib.utils.check(lambda: tab.showing) + addhw.find("Finish", "push button").click() + lib.utils.check(lambda: not addhw.active) + lib.utils.check(lambda: details.active) + + # Finish + details.find_fuzzy("Begin Installation", "button").click() + lib.utils.check(lambda: details.dead) + app.find_details_window(vmname) + + +def testNewKVMQ35UEFI(app): + """ + New VM that should default to Q35, and set UEFI + """ + app.uri = tests.utils.URIs.kvm + newvm = _open_newvm(app) + + newvm.find_fuzzy("Import", "radio").click() + _forward(newvm) + newvm.find("import-entry").set_text("/dev/default-pool/testvol1.img") + newvm.find("oslist-entry").set_text("fedora30") + popover = newvm.find("oslist-popover") + popover.find("include-eol").click() + popover.find_fuzzy("Fedora 30").click() + _forward(newvm) + _forward(newvm) + + # Select customize wizard, we will use this VM to + # hit some PPC64 code paths elsewhere + newvm.find_fuzzy("Customize", "check").click() + newvm.find_fuzzy("Finish", "button").click() + vmname = "fedora30" + details = app.find_details_window(vmname) + + # Change to UEFI + details.combo_check_default("Chipset:", "Q35") + details.combo_check_default("Firmware:", "BIOS") + details.combo_select("Firmware:", ".*x86_64.*") + details.find("config-apply").click() + + # Finish + details.find_fuzzy("Begin Installation", "button").click() + lib.utils.check(lambda: details.dead) + app.find_details_window(vmname) + + +def testNewPPC64(app): + """ + New PPC64 VM to test architecture selection + """ + app.uri = tests.utils.URIs.kvm + newvm = _open_newvm(app) + + newvm.find_fuzzy("Architecture options", "toggle").click() + newvm.combo_select("Architecture", ".*ppc64.*") + newvm.combo_check_default("Machine Type", ".*pseries.*") + + newvm.find_fuzzy("Manual", "radio").click() + _forward(newvm) + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(newvm) + _forward(newvm) + # Disable storage, we add some via customize + newvm.find_fuzzy("Enable storage", "check box").click() + _forward(newvm) + + # Select customize wizard, we will use this VM to + # hit some PPC64 code paths elsewhere + newvm.find_fuzzy("Customize", "check").click() + newvm.find_fuzzy("Finish", "button").click() + details = app.find_details_window("vm-ppc64") + + tab = details.find("overview-tab") + tab.combo_check_default("machine-combo", "pseries") + tab.combo_select("machine-combo", "pseries-2.1") + appl = details.find("config-apply") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Add a TPM SPAPR device + details.find("add-hardware", "push button").click() + addhw = app.find_window("Add New Virtual Hardware") + addhw.find("TPM", "table cell").click() + tab = addhw.find("tpm-tab", None) + lib.utils.check(lambda: tab.showing) + addhw.find("Finish", "push button").click() + lib.utils.check(lambda: not addhw.active) + lib.utils.check(lambda: details.active) + + # Add a SCSI disk which also adds virtio-scsi controller + details.find("add-hardware", "push button").click() + addhw = app.find_window("Add New Virtual Hardware") + addhw.find("Storage", "table cell").click() + tab = addhw.find("storage-tab", None) + lib.utils.check(lambda: tab.showing) + tab.combo_select("Bus type:", "SCSI") + addhw.find("Finish", "push button").click() + lib.utils.check(lambda: not addhw.active) + lib.utils.check(lambda: details.active) + + # Finish + details.find_fuzzy("Begin Installation", "button").click() + lib.utils.check(lambda: details.dead) + app.find_details_window("vm-ppc64") + + +def testNewVMAArch64UEFI(app): + """ + Test aarch64 UEFI usage + """ + app.uri = tests.utils.URIs.kvm_aarch64 + newvm = _open_newvm(app) + + newvm.find_fuzzy("Local install media", "radio").click() + _forward(newvm) + + newvm.find_fuzzy("Automatically detect", "check").click() + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + newvm.find("media-entry").set_text("/dev/default-pool/testvol1.img") + _forward(newvm) + _forward(newvm) + newvm.find_fuzzy("Enable storage", "check box").click() + _forward(newvm) + newvm.find_fuzzy("Finish", "button").click() + + app.find_details_window("vm1") + lib.utils.check(lambda: not newvm.showing) + + +def testNewVMArmKernel(app): + """ + New arm VM that requires kernel/initrd/dtb + """ + app.uri = tests.utils.URIs.kvm_armv7l_nodomcaps + newvm = _open_newvm(app) + + newvm.find_fuzzy("Architecture options", "toggle").click_expander() + newvm.find_fuzzy("Virt Type", "combo").click() + KVM = newvm.find_fuzzy("KVM", "menu item") + TCG = newvm.find_fuzzy("TCG", "menu item") + lib.utils.check(lambda: KVM.focused) + lib.utils.check(lambda: TCG.showing) + app.rawinput.pressKey("Esc") + + # Validate some initial defaults + local = newvm.find_fuzzy("Local", "radio") + lib.utils.check(lambda: not local.sensitive) + newvm.find_fuzzy("Machine Type", "combo").click() + app.sleep(.2) + newvm.find_fuzzy("canon", "menu item").click() + newvm.find_fuzzy("Machine Type", "combo").click() + app.sleep(.2) + newvm.find("virt", "menu item").click() + app.sleep(.5) + importradio = newvm.find("Import", "radio") + importradio.click() + lib.utils.check(lambda: importradio.checked) + _forward(newvm) + + newvm.find("import-entry").set_text("/dev/default-pool/default-vol") + # Make sure the info box shows up + newvm.find("Kernel/initrd settings can be configured") + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(newvm, check=False) + + # Disk collision box pops up, hit ok + app.click_alert_button("in use", "Yes") + + _forward(newvm) + newvm.find_fuzzy("Finish", "button").click() + + lib.utils.check(lambda: not newvm.showing) + app.find_details_window("vm1") + + + +def testNewVMContainerApp(app): + """ + Simple LXC app install + """ + app.uri = tests.utils.URIs.lxc + + newvm = _open_newvm(app) + newvm.find_fuzzy("Application", "radio").click() + _forward(newvm) + + # Set custom init + apptext = newvm.find_fuzzy(None, "text", "application path") + apptext.set_text("") + _forward(newvm, check=False) + app.click_alert_button("path is required", "OK") + newvm.find("install-app-browse").click() + app.select_storagebrowser_volume("default-pool", "aaa-unused.qcow2") + lib.utils.check(lambda: "aaa-unused.qcow2" in apptext.text) + + _forward(newvm) + _forward(newvm) + # Trigger back, to ensure disk page skipping works + _back(newvm) + _back(newvm) + _forward(newvm) + _forward(newvm) + + # Select customize wizard, we will use this VM to hit specific + # code paths + newvm.find_fuzzy("Customize", "check").click() + newvm.find_fuzzy("Finish", "button").click() + vmname = "container1" + details = app.find_details_window(vmname) + + # Tweak init values + details.find("Boot Options", "table cell").click() + tab = details.find("boot-tab") + tab.find("Init path:", "text").set_text("") + tab.find("Init args:", "text").set_text("some args") + appl = details.find("config-apply") + appl.click() + app.click_alert_button("init path must be specified", "OK") + lib.utils.check(lambda: appl.sensitive) + tab.find("Init path:", "text").set_text("/some/path") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Check that addhw container options are disabled + details.find("add-hardware", "push button").click() + addhw = app.find_window("Add New Virtual Hardware") + addhw.find("PCI Host Device", "table cell").click() + # Ensure the error label is showing + label = addhw.find("Not supported for containers") + label.check_onscreen() + addhw.find("Cancel", "push button").click() + lib.utils.check(lambda: not addhw.active) + lib.utils.check(lambda: details.active) + + # Finish + details.find_fuzzy("Begin Installation", "button").click() + lib.utils.check(lambda: not newvm.showing) + app.find_details_window(vmname) + + +def testNewVMCustomizeCancel(app): + """ + Test cancelling out of the customize wizard + """ + newvm = _open_newvm(app) + newvm.find_fuzzy("Manual", "radio").click() + _forward(newvm) + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(newvm) + _forward(newvm) + _forward(newvm) + + newvm.find_fuzzy("Customize", "check").click() + newvm.find_fuzzy("Finish", "button").click() + vmname = "vm1" + details = app.find_details_window(vmname) + + details.find("Cancel Installation", "push button").click() + app.click_alert_button("abort the installation", "No") + lib.utils.check(lambda: details.active) + details.find("Cancel Installation", "push button").click() + app.click_alert_button("abort the installation", "Yes") + lib.utils.check(lambda: not details.active) + lib.utils.check(lambda: not newvm.active) + + +def testNewVMCustomizeMisc(app): + """ + Some specific customize logic paths + """ + newvm = _open_newvm(app) + newvm.find_fuzzy("Manual", "radio").click() + _forward(newvm) + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(newvm) + _forward(newvm) + _forward(newvm) + + newvm.find_fuzzy("Customize", "check").click() + newvm.find_fuzzy("Finish", "button").click() + vmname = "vm1" + details = app.find_details_window(vmname) + + # Test name change + tab = details.find("overview-tab") + nametext = tab.find("Name:", "text") + nametext.set_text("foonewname") + details.find("config-apply").click() + app.find_details_window("foonewname") + + # Trigger XML failure to hit some codepaths + nametext.set_text("") + details.find("Begin Installation").click() + app.click_alert_button("unapplied changes", "Yes") + app.click_alert_button("name must be specified", "Close") + lib.utils.check(lambda: details.showing) + + # Discard XML change and continue with install + details.find("Begin Installation").click() + app.click_alert_button("unapplied changes", "No") + lib.utils.check(lambda: not details.showing) + lib.utils.check(lambda: not newvm.showing) + app.find_details_window("foonewname") + + + +def testNewVMContainerTree(app): + """ + Simple LXC tree install + """ + app.uri = tests.utils.URIs.lxc + + newvm = _open_newvm(app) + newvm.find_fuzzy("Operating system", "radio").click() + _forward(newvm) + + # Set directory path + dirtext = newvm.find_fuzzy(None, "text", "root directory") + dirtext.set_text("") + _forward(newvm, check=False) + app.click_alert_button("path is required", "OK") + + newvm.find("install-oscontainer-browse").click() + app.select_storagebrowser_volume("default-pool", "dir-vol") + lib.utils.check(lambda: "dir-vol" in dirtext.text) + + _forward(newvm) + _forward(newvm) + newvm.find_fuzzy("Finish", "button").click() + + lib.utils.check(lambda: not newvm.showing) + app.find_details_window("container1") + + + +def testNewVMContainerVZ(app): + """ + Virtuozzo container install + """ + app.uri = tests.utils.URIs.vz + + newvm = _open_newvm(app) + newvm.find_fuzzy("Container", "radio").click() + newvm.find_fuzzy("Virtual machine", "radio").click() + newvm.find_fuzzy("Container", "radio").click() + _forward(newvm) + + # Set directory path + templatetext = newvm.find_fuzzy(None, "text", "container template") + templatetext.set_text("") + _forward(newvm, check=False) + app.click_alert_button("template name is required", "OK") + templatetext.set_text("centos-6-x86_64") + _forward(newvm) + _forward(newvm) + newvm.find_fuzzy("Finish", "button").click() + + app.find_details_window("container1") + lib.utils.check(lambda: not newvm.showing) + + + +def testNewVMContainerBootstrap(app): + app.uri = tests.utils.URIs.lxc + try: + import virtBootstrap # pylint: disable=unused-import + except ImportError: + pytest.skip("virtBootstrap not installed") + + newvm = _open_newvm(app) + newvm.find_fuzzy("Operating system", "radio").click() + _forward(newvm) + + # Set directory path + import tempfile + tmpdir = tempfile.TemporaryDirectory() + newvm.find_fuzzy("Create OS directory", "check box").click() + + uritext = newvm.find("install-oscontainer-source-uri") + uritext.text = "" + _forward(newvm, check=False) + app.click_alert_button("Source URL is required", "OK") + uritext.text = "docker://alpine" + + rootdir = newvm.find_fuzzy(None, "text", "root directory") + lib.utils.check(lambda: ".local/share/libvirt" in rootdir.text) + rootdir.set_text("/dev/null") + _forward(newvm, check=False) + app.click_alert_button("not directory", "OK") + rootdir.set_text("/root") + _forward(newvm, check=False) + app.click_alert_button("No write permissions", "OK") + rootdir.set_text("/tmp") + _forward(newvm, check=False) + app.click_alert_button("directory is not empty", "No") + rootdir.set_text(tmpdir.name) + newvm.find("install-oscontainer-root-passwd").set_text("foobar") + # Invalid credentials to trigger failure + newvm.find("Credentials", "toggle button").click_expander() + newvm.find("bootstrap-registry-user").set_text("foo") + _forward(newvm, check=None) + app.click_alert_button("Please specify password", "OK") + newvm.find("bootstrap-registry-password").set_text("bar") + + _forward(newvm) + _forward(newvm) + newvm.find_fuzzy("Finish", "button").click() + app.click_alert_button("virt-bootstrap did not complete", "Close") + _back(newvm) + _back(newvm) + newvm.find("bootstrap-registry-user").set_text("") + newvm.find("bootstrap-registry-password").set_text("") + + _forward(newvm) + _forward(newvm) + newvm.find_fuzzy("Finish", "button").click() + prog = app.find_window("Creating Virtual Machine") + lib.utils.check(lambda: not prog.showing, timeout=30) + + lib.utils.check(lambda: not newvm.showing) + app.find_details_window("container1") + + + +def testNewVMXenPV(app): + """ + Test the create wizard with a fake xen PV install + """ + app.uri = tests.utils.URIs.xen + newvm = _open_newvm(app) + + newvm.find_fuzzy("Architecture options", "toggle").click() + newvm.combo_select("Xen Type", ".*paravirt.*") + + newvm.find_fuzzy("Import", "radio").click() + _forward(newvm) + newvm.find("import-entry").set_text("/dev/default-pool/testvol1.img") + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(newvm) + _forward(newvm) + newvm.find_fuzzy("Finish", "button").click() + + app.find_details_window("vm1") + lib.utils.check(lambda: not newvm.showing) + + + +def testNewVMInstallFail(app): + def dofail(): + _newvm = _open_newvm(app) + _newvm.find_fuzzy("Manual", "radio").click() + _forward(_newvm) + _newvm.find("oslist-entry").set_text("generic") + _newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(_newvm) + _forward(_newvm) + _forward(_newvm) + + # '/' in name will trigger libvirt error + _newvm.find_fuzzy("Name", "text").set_text("test/bad") + _newvm.find_fuzzy("Finish", "button").click() + app.click_alert_button("Unable to complete install", "Close") + return _newvm + + newvm = dofail() + pathlabel = newvm.find(".*test/bad.qcow2") + generatedpath = pathlabel.text + # Changing VM name should not generate a new path + newvm.find_fuzzy("Name", "text").set_text("test/badfoo") + lib.utils.check(lambda: pathlabel.text == generatedpath) + newvm.find_fuzzy("Finish", "button").click() + app.click_alert_button("Unable to complete install", "Close") + # Closing dialog should trigger storage cleanup path + newvm.find_fuzzy("Cancel", "button").click() + lib.utils.check(lambda: not newvm.visible) + + # Run again + newvm = dofail() + _back(newvm) + newvm.find_fuzzy("Select or create", "radio").click() + + newvm.find("storage-entry").set_text("/dev/default-pool/somenewvol1") + _forward(newvm) + newvm.find_fuzzy("Name", "text").set_text("test-foo") + newvm.find_fuzzy("Finish", "button").click() + + app.find_details_window("test-foo") + lib.utils.check(lambda: not newvm.showing) + + + +def testNewVMCustomizeXMLEdit(app): + """ + Test new VM with raw XML editing via customize wizard + """ + app.open(xmleditor_enabled=True) + newvm = _open_newvm(app) + + # Create a custom named VM, using CDROM media, and default storage + vmname = "fooxmleditvm" + newvm.find_fuzzy("Local install media", "radio").click() + newvm.find_fuzzy("Forward", "button").click() + nonexistpath = "/dev/foovmm-idontexist" + existpath = "/dev/default-pool/testvol1.img" + newvm.find("media-entry").set_text(nonexistpath) + lib.utils.check( + lambda: newvm.find("oslist-entry").text == "None detected") + newvm.find_fuzzy("Automatically detect", "check").click() + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(newvm, check=False) + app.click_alert_button("Error setting installer", "OK") + newvm.find("media-entry").set_text(existpath) + _forward(newvm) + _forward(newvm) + _forward(newvm) + newvm.find_fuzzy("Customize", "check").click() + newvm.find_fuzzy("Name", "text").set_text(vmname) + newvm.find_fuzzy("Finish", "button").click() + win = app.find_details_window(vmname) + xmleditor = win.find("XML editor") + finish = win.find("config-apply") + + # Change a device setting with the XML editor + win.find_fuzzy("IDE Disk 1", "table cell").click() + tab = win.find("disk-tab") + win.find("XML", "page tab").click() + # Change the disk path via the XML editor + fname = vmname + ".qcow2" + lib.utils.check(lambda: fname in xmleditor.text) + newx = xmleditor.text.replace(fname, "default-vol") + xmleditor.set_text(newx) + appl = win.find("config-apply") + # This is kindof a bug, changing path in XML editor in Customize + # doesn't take effect for storage with creation parameters, but + # it's a pain to fix. + appl.click() + lib.utils.check(lambda: not appl.sensitive) + lib.utils.check(lambda: vmname in xmleditor.text) + + # Change a VM setting and verify it + win.find_fuzzy("Boot", "table cell").click() + tab = win.find("boot-tab") + bootmenu = tab.find("Enable boot menu", "check box") + lib.utils.check(lambda: not bootmenu.checked) + win.find("XML", "page tab").click() + newtext = xmleditor.text.replace( + "", "") + xmleditor.set_text(newtext) + finish.click() + win.find("Details", "page tab").click() + lib.utils.check(lambda: bootmenu.checked) + + # Change a device setting with the XML editor + win.find_fuzzy("NIC", "table cell").click() + tab = win.find("network-tab") + win.find("XML", "page tab").click() + newbrname = "BRFAKE" + newx = xmleditor.text.replace("network", "bridge") + newx = newx.replace('bridge="default"', "bridge='%s'" % newbrname) + xmleditor.set_text(newx) + finish.click() + + # Finish install. + win.find_fuzzy("Begin Installation", "button").click() + lib.utils.check(lambda: win.dead) + win = app.find_details_window(vmname) + win.find("Details", "radio button").click() + + # Verify VM change stuck + win.find_fuzzy("Boot", "table cell").click() + tab = win.find("boot-tab") + bootmenu = tab.find("Enable boot menu", "check box") + lib.utils.check(lambda: bootmenu.checked) + + # Verify device change stuck + win.find_fuzzy("NIC", "table cell").click() + tab = win.find("network-tab") + devname = tab.find("Device name:", "text") + lib.utils.check(lambda: devname.text == newbrname) + + # Verify install media is handled correctly after XML customize + win.find_fuzzy("IDE CDROM 1", "table cell").click() + tab = win.find("disk-tab") + mediaent = tab.find("media-entry") + lib.utils.check(lambda: mediaent.text == existpath) + win.find("Shut Down", "push button").click() + run = win.find("Run", "push button") + lib.utils.check(lambda: run.sensitive) + lib.utils.check(lambda: mediaent.text == "") + + # Verify default disk storage was actually created. This has some + # special handling in domain.py + tab.find("Browse", "push button").click() + browser = app.root.find("vmm-storage-browser") + browser.find("%s.qcow2" % vmname, "table cell") + + +def testNewVMRemote(app): + """ + Hit some is_remote code paths + """ + app.uri = tests.utils.URIs.test_remote + newvm = _open_newvm(app) + + newvm.find_fuzzy("Import", "radio").click() + _forward(newvm) + importtext = newvm.find("import-entry") + + # Click forward, hitting missing Import path error + _forward(newvm, check=False) + app.click_alert_button("import is required", "OK") + + # Click forward, but Import path doesn't exist + importtext.set_text("/dev/default-pool/idontexist") + _forward(newvm, check=False) + app.click_alert_button("import path must point", "OK") + importtext.set_text("/dev/default-pool/default-vol") + + # Click forward, hitting missing OS error + _forward(newvm, check=False) + app.click_alert_button("select an OS", "OK") + + # Set OS + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + + # Click forward, but Import path is in use, and exit + _forward(newvm, check=False) + app.click_alert_button("in use", "No") + + # storagebrowser bits + newvm.find("install-import-browse").click() + browsewin = app.root.find("vmm-storage-browser") + # Insensitive for remote connection + browselocal = browsewin.find("Browse Local") + lib.utils.check(lambda: browselocal.sensitive is False) + # Close the browser and reopen + browsewin.find("Cancel").click() + lib.utils.check(lambda: not browsewin.active) + # Reopen, select storage + newvm.find("install-import-browse").click() + app.select_storagebrowser_volume("default-pool", "bochs-vol") + lib.utils.check( + lambda: importtext.text == "/dev/default-pool/bochs-vol") + + _forward(newvm) + _forward(newvm) + + newvm.find_fuzzy("Finish", "button").click() + app.find_details_window("vm1") + lib.utils.check(lambda: not newvm.showing) + + +def testNewVMSession(app): + """ + Test with fake qemu session + """ + app.uri = tests.utils.URIs.kvm_session + newvm = _open_newvm(app) + + newvm.find_fuzzy("Import", "radio").click() + _forward(newvm) + newvm.find("import-entry").set_text("/dev/default-pool/testvol1.img") + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(newvm) + _forward(newvm) + newvm.combo_check_default("net-source", "Usermode") + + newvm.find_fuzzy("Finish", "button").click() + details = app.find_details_window("vm1") + lib.utils.check(lambda: not newvm.showing) + app.sleep(1) + details.window_close() + + # Ensure disconnecting will close the dialog + manager = app.topwin + manager.window_maximize() + newvm = _open_newvm(app) + manager.click_title() + app.manager_conn_disconnect(".*session.*") + lib.utils.check(lambda: not newvm.showing) + + +def testNewVMEmptyConn(app): + """ + Test with an empty connection + """ + app.uri = tests.utils.URIs.test_empty + newvm = _open_newvm(app) + + newvm.find_fuzzy("Import", "radio").click() + _forward(newvm) + newvm.find("import-entry").set_text(__file__) + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(newvm) + _forward(newvm) + newvm.combo_check_default("net-source", "Bridge") + warnlabel = newvm.find_fuzzy("suitable default network", "label") + warnlabel.check_onscreen() + newvm.find("Device name:", "text").set_text("foobr0") + + # Select customize wizard, we will use this VM to hit specific + # code paths + newvm.find_fuzzy("Customize", "check").click() + newvm.find_fuzzy("Finish", "button").click() + vmname = "vm1" + details = app.find_details_window(vmname) + + # Check that addhw hostdev drop down is empty + details.find("add-hardware", "push button").click() + addhw = app.find_window("Add New Virtual Hardware") + addhw.find("USB Host Device", "table cell").click() + tab = addhw.find("host-tab", None) + lib.utils.check(lambda: tab.showing) + cell = tab.find("No Devices", "table cell") + lib.utils.check(lambda: cell.selected) + addhw.find("Cancel", "push button").click() + lib.utils.check(lambda: not addhw.active) + lib.utils.check(lambda: details.active) + + # Finish + details.find_fuzzy("Begin Installation", "button").click() + lib.utils.check(lambda: details.dead) + app.find_details_window(vmname) + + +def testNewVMInactiveNetwork(app): + """ + Test with an inactive 'default' network + """ + app.uri = tests.utils.URIs.test_default + hostwin = app.manager_open_host("Virtual Networks", + conn_label="test default") + cell = hostwin.find("default", "table cell") + cell.click() + hostwin.find("net-stop").click() + hostwin.keyCombo("w") + + newvm = _open_newvm(app) + + newvm.find_fuzzy("Import", "radio").click() + _forward(newvm) + newvm.find("import-entry").set_text(__file__) + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(newvm) + _forward(newvm) + + newvm.find_fuzzy("Finish", "button").click() + app.click_alert_button("start the network", "Yes") + lib.utils.check(lambda: not newvm.showing) + + +@unittest.mock.patch.dict('os.environ', {"VIRTINST_TEST_SUITE": "1"}) +def testNewVMDefaultBridge(app): + """ + We actually set the unittest env variable here, which + sets a fake bridge in interface.py + """ + app.uri = tests.utils.URIs.test_empty + newvm = _open_newvm(app) + + newvm.find_fuzzy("Import", "radio").click() + _forward(newvm) + newvm.find("import-entry").set_text(__file__) + newvm.find("oslist-entry").set_text("generic") + newvm.find("oslist-popover").find_fuzzy("generic").click() + _forward(newvm) + _forward(newvm) + newvm.find("Network selection", "toggle button").click_expander() + newvm.combo_check_default("net-source", "Bridge") + devname = newvm.find("Device name:", "text") + lib.utils.check(lambda: devname.text == "testsuitebr0") + + newvm.find_fuzzy("Finish", "button").click() + app.find_details_window("vm1") + lib.utils.check(lambda: not newvm.showing) diff --git a/tests/uitests/test_createvol.py b/tests/uitests/test_createvol.py index 4fc0bb52..c86c4164 100644 --- a/tests/uitests/test_createvol.py +++ b/tests/uitests/test_createvol.py @@ -4,158 +4,154 @@ from . import lib -class CreateVol(lib.testcase.UITestCase): +##################################### +# UI tests for the createvol wizard # +##################################### + +def _open_createvol(app, hostwin): + hostwin.find("vol-new", "push button").click() + win = app.find_window("Add a Storage Volume") + lib.utils.check(lambda: win.active) + return win + + +def testCreateVolDefault(app): """ - UI tests for the createvol wizard + Create default volume, clean it up """ + hostwin = app.manager_open_host("Storage") + poolcell = hostwin.find("default-pool", "table cell") + poolcell.click() + vollist = hostwin.find("vol-list", "table") + win = _open_createvol(app, hostwin) + finish = win.find("Finish", "push button") + name = win.find("Name:", "text") - def _open_create_win(self, hostwin): - hostwin.find("vol-new", "push button").click() - win = self.app.root.find( - "Add a Storage Volume", "frame") - lib.utils.check(lambda: win.active) - return win + # Create a default qcow2 volume + lib.utils.check(lambda: name.text == "vol") + newname = "a-newvol" + name.set_text(newname) + win.find("Max Capacity:", "spin button").set_text("10.5") + finish.click() + + # Delete it, clicking 'No' first + volcell = vollist.find(newname + ".qcow2") + volcell.click() + hostwin.find("vol-refresh", "push button").click() + hostwin.find("vol-delete", "push button").click() + app.click_alert_button("permanently delete the volume", "No") + volcell = vollist.find(newname + ".qcow2") + hostwin.find("vol-delete", "push button").click() + app.click_alert_button("permanently delete the volume", "Yes") + lib.utils.check(lambda: volcell.dead) + + # Ensure host window closes fine + hostwin.keyCombo("w") + lib.utils.check(lambda: not hostwin.showing and + not hostwin.active) - ############## - # Test cases # - ############## +def testCreateVolMisc(app): + """ + Cover all createvol options + """ + hostwin = app.manager_open_host("Storage") + poolcell = hostwin.find("default-pool", "table cell") + poolcell.click() + win = _open_createvol(app, hostwin) + name = win.find("Name:", "text") + finish = win.find("Finish", "push button") + vollist = hostwin.find("vol-list", "table") - def testCreateVolDefault(self): - """ - Create default volume, clean it up - """ - hostwin = self.app.open_host_window("Storage") - poolcell = hostwin.find("default-pool", "table cell") - poolcell.click() - vollist = hostwin.find("vol-list", "table") - win = self._open_create_win(hostwin) - finish = win.find("Finish", "push button") - name = win.find("Name:", "text") + # Create a qcow2 with backing file + newname = "aaa-qcow2-backing.qcow2" + name.set_text(newname) + win.combo_select("Format:", "qcow2") + win.find("Backing store").click_expander() + win.find("Browse...").click() + browsewin = app.root.find("vmm-storage-browser") + # Test cancel button + browsewin.find("Cancel", "push button").click() + lib.utils.check(lambda: not browsewin.active) + win.find("Browse...").click() + browsewin = app.root.find("vmm-storage-browser") + # Test browse local opening + browsewin.find("Browse Local", "push button").click() + chooser = app.root.find( + "Locate existing storage", "file chooser") + chooser.keyCombo("F4") + app.select_storagebrowser_volume( + "default-pool", "bochs-vol", doubleclick=True) + backingstore = win.find("backing-store") + lib.utils.check(lambda: "bochs-vol" in backingstore.text) + finish.click() + vollist.find(newname) - # Create a default qcow2 volume - lib.utils.check(lambda: name.text == "vol") - newname = "a-newvol" - name.set_text(newname) - win.find("Max Capacity:", "spin button").set_text("10.5") - finish.click() + # Create a raw volume with some size tweaking + win = _open_createvol(app, hostwin) + # Using previous name so we collide + name.set_text(newname) + win.combo_select("Format:", "raw") + cap = win.find("Max Capacity:", "spin button") + alloc = win.find("Allocation:", "spin button") + alloc.set_text("50.0") + alloc.click() + app.rawinput.pressKey("Enter") + lib.utils.check(lambda: cap.text == "50.0") + cap.set_text("1.0") + cap.click() + app.rawinput.pressKey("Enter") + lib.utils.check(lambda: alloc.text == "1.0") + alloc.set_text("0.5") + alloc.click() + app.rawinput.pressKey("Enter") + lib.utils.check(lambda: cap.text == "1.0") - # Delete it, clicking 'No' first - volcell = vollist.find(newname + ".qcow2") - volcell.click() - hostwin.find("vol-refresh", "push button").click() - hostwin.find("vol-delete", "push button").click() - self.app.click_alert_button("permanently delete the volume", "No") - volcell = vollist.find(newname + ".qcow2") - hostwin.find("vol-delete", "push button").click() - self.app.click_alert_button("permanently delete the volume", "Yes") - lib.utils.check(lambda: volcell.dead) + finish.click() + app.click_alert_button("Error validating volume", "Close") + newname = "a-newvol.raw" + name.set_text(newname) + finish.click() + vollist.find(newname) - # Ensure host window closes fine - hostwin.keyCombo("w") - lib.utils.check(lambda: not hostwin.showing and - not hostwin.active) - - def testCreateVolMisc(self): - """ - Cover all createvol options - """ - hostwin = self.app.open_host_window("Storage") - poolcell = hostwin.find("default-pool", "table cell") - poolcell.click() - win = self._open_create_win(hostwin) - name = win.find("Name:", "text") - finish = win.find("Finish", "push button") - vollist = hostwin.find("vol-list", "table") - - # Create a qcow2 with backing file - newname = "aaa-qcow2-backing.qcow2" - name.set_text(newname) - win.combo_select("Format:", "qcow2") - win.find("Backing store").click_expander() - win.find("Browse...").click() - browsewin = self.app.root.find("vmm-storage-browser") - # Test cancel button - browsewin.find("Cancel", "push button").click() - lib.utils.check(lambda: not browsewin.active) - win.find("Browse...").click() - browsewin = self.app.root.find("vmm-storage-browser") - # Test browse local opening - browsewin.find("Browse Local", "push button").click() - chooser = self.app.root.find( - "Locate existing storage", "file chooser") - chooser.keyCombo("F4") - self.app.select_storagebrowser_volume( - "default-pool", "bochs-vol", doubleclick=True) - backingstore = win.find("backing-store") - lib.utils.check(lambda: "bochs-vol" in backingstore.text) - finish.click() - vollist.find(newname) - - # Create a raw volume with some size tweaking - win = self._open_create_win(hostwin) - # Using previous name so we collide - name.set_text(newname) - win.combo_select("Format:", "raw") - cap = win.find("Max Capacity:", "spin button") - alloc = win.find("Allocation:", "spin button") - alloc.set_text("50.0") - alloc.click() - self.app.rawinput.pressKey("Enter") - lib.utils.check(lambda: cap.text == "50.0") - cap.set_text("1.0") - cap.click() - self.app.rawinput.pressKey("Enter") - lib.utils.check(lambda: alloc.text == "1.0") - alloc.set_text("0.5") - alloc.click() - self.app.rawinput.pressKey("Enter") - lib.utils.check(lambda: cap.text == "1.0") - - finish.click() - self.app.click_alert_button("Error validating volume", "Close") - newname = "a-newvol.raw" - name.set_text(newname) - finish.click() - vollist.find(newname) - - # Create LVM backing store - hostwin.find("disk-pool", "table cell").click() - win = self._open_create_win(hostwin) - newname = "aaa-lvm" - name.set_text(newname) - win.find("Backing store").click_expander() - win.find("Browse...").click() - self.app.select_storagebrowser_volume("disk-pool", "diskvol7") - finish.click() - vollist.find(newname) + # Create LVM backing store + hostwin.find("disk-pool", "table cell").click() + win = _open_createvol(app, hostwin) + newname = "aaa-lvm" + name.set_text(newname) + win.find("Backing store").click_expander() + win.find("Browse...").click() + app.select_storagebrowser_volume("disk-pool", "diskvol7") + finish.click() + vollist.find(newname) - def testCreateVolXMLEditor(self): - self.app.open(xmleditor_enabled=True) - hostwin = self.app.open_host_window("Storage") - poolcell = hostwin.find("default-pool", "table cell") - poolcell.click() - win = self._open_create_win(hostwin) - finish = win.find("Finish", "push button") - name = win.find("Name:", "text") - vollist = hostwin.find("vol-list", "table") - # Create a new obj with XML edited name, verify it worked - tmpname = "objtmpname" - newname = "aafroofroo" - name.set_text(tmpname) - win.find("XML", "page tab").click() - xmleditor = win.find("XML editor") - newtext = xmleditor.text.replace( - ">%s.qcow2<" % tmpname, ">%s<" % newname) - xmleditor.set_text(newtext) - finish.click() - lib.utils.check(lambda: hostwin.active) - vollist.find(newname) +def testCreateVolXMLEditor(app): + app.open(xmleditor_enabled=True) + hostwin = app.manager_open_host("Storage") + poolcell = hostwin.find("default-pool", "table cell") + poolcell.click() + win = _open_createvol(app, hostwin) + finish = win.find("Finish", "push button") + name = win.find("Name:", "text") + vollist = hostwin.find("vol-list", "table") - # Do standard xmleditor tests - win = self._open_create_win(hostwin) - lib.utils.test_xmleditor_interactions(self.app, win, finish) - win.find("Cancel", "push button").click() - lib.utils.check(lambda: not win.visible) + # Create a new obj with XML edited name, verify it worked + tmpname = "objtmpname" + newname = "aafroofroo" + name.set_text(tmpname) + win.find("XML", "page tab").click() + xmleditor = win.find("XML editor") + newtext = xmleditor.text.replace( + ">%s.qcow2<" % tmpname, ">%s<" % newname) + xmleditor.set_text(newtext) + finish.click() + lib.utils.check(lambda: hostwin.active) + vollist.find(newname) + + # Do standard xmleditor tests + win = _open_createvol(app, hostwin) + lib.utils.test_xmleditor_interactions(app, win, finish) + win.find("Cancel", "push button").click() + lib.utils.check(lambda: not win.visible) diff --git a/tests/uitests/test_delete.py b/tests/uitests/test_delete.py index ce4433f7..57c1fafd 100644 --- a/tests/uitests/test_delete.py +++ b/tests/uitests/test_delete.py @@ -23,7 +23,7 @@ class _DeleteRow: def _create_testdriver_path(fn): - def wrapper(self, *args, **kwargs): + def wrapper(app, *args, **kwargs): # This special path is hardcoded in test-many-devices tmppath = "/tmp/virt-manager-uitests/tmp1" tmpdir = os.path.dirname(tmppath) @@ -32,7 +32,7 @@ def _create_testdriver_path(fn): os.mkdir(tmpdir) open(tmppath, "w").write("foo") os.chmod(tmppath, 0o444) - return fn(self, tmppath, *args, **kwargs) + return fn(app, tmppath, *args, **kwargs) finally: if os.path.exists(tmpdir): os.chmod(tmpdir, 0o777) @@ -40,267 +40,276 @@ def _create_testdriver_path(fn): return wrapper -class Delete(lib.testcase.UITestCase): - """ - UI tests for virt-manager's VM delete window - """ - def _open_storage_browser(self): - self.app.root.find("New", "push button").click() - newvm = self.app.root.find("New VM", "frame") - newvm.find_fuzzy("Local install media", "radio").click() - newvm.find_fuzzy("Forward", "button").click() - newvm.find_fuzzy("install-iso-browse", "button").click() - return self.app.root.find("vmm-storage-browser") +def _open_storage_browser(app): + app.root.find("New", "push button").click() + newvm = app.find_window("New VM") + newvm.find_fuzzy("Local install media", "radio").click() + newvm.find_fuzzy("Forward", "button").click() + newvm.find_fuzzy("install-iso-browse", "button").click() + return app.root.find("vmm-storage-browser") - def _open_delete(self, vmname): - manager = self.app.topwin - cell = manager.find(vmname, "table cell") - cell.click() - cell.click(button=3) - menu = self.app.root.find("vm-action-menu") - menu.find("Delete", "menu item").click() - return self.app.root.find_fuzzy("Delete", "frame") +def _open_delete(app, vmname): + app.manager_vm_action(vmname, delete=True) + return app.find_window("Delete") - def _finish(self, delete, paths, expect_fail=False, click_no=False): - delete.find_fuzzy("Delete", "button").click() - if paths: - alert = self.app.root.find("vmm dialog", "alert") - alert.find_fuzzy("Are you sure") - for path in paths: - alert.find_fuzzy(path) - if click_no: - alert.find("No", "push button").click() - return - alert.find("Yes", "push button").click() - if not expect_fail: - lib.utils.check(lambda: not delete.showing) - def _get_all_rows(self, delete): +def _finish(app, delete, paths, expect_fail=False, click_no=False): + delete.find_fuzzy("Delete", "button").click() + if paths: + alert = app.root.find("vmm dialog", "alert") + alert.find_fuzzy("Are you sure") + for path in paths: + alert.find_fuzzy(path) + if click_no: + alert.find("No", "push button").click() + return + alert.find("Yes", "push button").click() + if not expect_fail: + lib.utils.check(lambda: not delete.showing) + + +def _get_all_rows(delete): + slist = delete.find("storage-list") + def pred(node): + return node.roleName == "table cell" + cells = slist.findChildren(pred, isLambda=True) + + idx = 0 + rows = [] + while idx < len(cells): + rows.append(_DeleteRow(*cells[idx:idx + 4])) + idx += 4 + return rows + + +################################################ +# UI tests for virt-manager's VM delete window # +################################################ + +def _testDeleteManyDevices(app, + nondefault_path=None, delete_nondefault=False, + skip_finish=False): + delete = _open_delete(app, "test-many-devices") + + rows = _get_all_rows(delete) + selected_rows = [r.path for r in rows if r.default] + undeletable_rows = [r.path for r in rows if r.undeletable] + notdefault_rows = [r.path for r in rows if r.notdefault] + + defpath = "/dev/default-pool/overlay.img" + nondefault_path2 = "/dev/default-pool/sharevol.img" + + assert selected_rows == [defpath] + if nondefault_path: + assert nondefault_path in notdefault_rows + assert nondefault_path2 in notdefault_rows + assert "/dev/fda" in undeletable_rows + + if delete_nondefault: + # Click the selector for the nondefault path + found = [r for r in rows if r.path == nondefault_path] + assert len(found) == 1 slist = delete.find("storage-list") - def pred(node): - return node.roleName == "table cell" - cells = slist.findChildren(pred, isLambda=True) + slist.click() + chkcell = found[0].chkcell + chkcell.bring_on_screen() + chkcell.click() + chkcell.click() + chkcell.click() + lib.utils.check(lambda: chkcell.checked) - idx = 0 - rows = [] - while idx < len(cells): - rows.append(_DeleteRow(*cells[idx:idx + 4])) - idx += 4 - return rows + paths = [] + if defpath: + paths.append(defpath) + if delete_nondefault: + paths.append(nondefault_path) + if skip_finish: + return paths + _finish(app, delete, paths) + + # Confirm + browser = _open_storage_browser(app) + browser.find_fuzzy("default-pool", "table cell").click() + browser.find("vol-refresh", "push button").click() + lib.utils.check(lambda: "overlay.img" not in browser.fmt_nodes()) + browser.find("sharevol.img", "table cell") - ############## - # Test cases # - ############## +@_create_testdriver_path +def testDeleteManyDevices(app, tmppath): + """ + Hit a specific case of a path not selected by default + because the permissions are readonly + """ + _testDeleteManyDevices(app, nondefault_path=tmppath) - def _testDeleteManyDevices(self, - nondefault_path=None, delete_nondefault=False, - skip_finish=False): - delete = self._open_delete("test-many-devices") - rows = self._get_all_rows(delete) - selected_rows = [r.path for r in rows if r.default] - undeletable_rows = [r.path for r in rows if r.undeletable] - notdefault_rows = [r.path for r in rows if r.notdefault] +@_create_testdriver_path +def testDeleteNondefaultOverride(app, tmppath): + """ + Path not selected by default, but we select it, + which will cause it to be manually unlinked + """ + _testDeleteManyDevices(app, + nondefault_path=tmppath, + delete_nondefault=True) + assert not os.path.exists(tmppath) - defpath = "/dev/default-pool/overlay.img" - nondefault_path2 = "/dev/default-pool/sharevol.img" - assert selected_rows == [defpath] - if nondefault_path: - assert nondefault_path in notdefault_rows - assert nondefault_path2 in notdefault_rows - assert "/dev/fda" in undeletable_rows +@_create_testdriver_path +def testDeleteFailure(app, tmppath): + """ + After launching the wizard we change permissions to make + file deletion fail + """ + paths = _testDeleteManyDevices(app, + nondefault_path=tmppath, + delete_nondefault=True, + skip_finish=True) + os.chmod(os.path.dirname(tmppath), 0o555) + delete = app.find_window("Delete") + _finish(app, delete, paths, expect_fail=True, click_no=True) + lib.utils.check(lambda: delete.active) + _finish(app, delete, paths, expect_fail=True) + assert os.path.exists(tmppath) + app.click_alert_button("Errors encountered", "Close") - if delete_nondefault: - # Click the selector for the nondefault path - found = [r for r in rows if r.path == nondefault_path] - assert len(found) == 1 - slist = delete.find("storage-list") - slist.click() - chkcell = found[0].chkcell - chkcell.bring_on_screen() - chkcell.click() - chkcell.click() - chkcell.click() - lib.utils.check(lambda: chkcell.checked) + # Ensure disconnecting will close the dialog + manager = app.topwin + manager.window_maximize() + win = _open_delete(app, "test-clone") + manager.click_title() + app.manager_conn_disconnect("test testdriver.xml") + lib.utils.check(lambda: not win.showing) - paths = [] - if defpath: - paths.append(defpath) - if delete_nondefault: - paths.append(nondefault_path) - if skip_finish: - return paths - self._finish(delete, paths) - # Confirm - browser = self._open_storage_browser() - browser.find_fuzzy("default-pool", "table cell").click() - browser.find("vol-refresh", "push button").click() - lib.utils.check(lambda: "overlay.img" not in browser.fmt_nodes()) - browser.find("sharevol.img", "table cell") +def testDeleteRemoteManyDevices(app): + """ + Test with a remote VM to hit a certain code path + """ + app.uri = tests.utils.URIs.kvm_remote + _testDeleteManyDevices(app) - @_create_testdriver_path - def testDeleteManyDevices(self, tmppath): - """ - Hit a specific case of a path not selected by default - because the permissions are readonly - """ - self._testDeleteManyDevices(nondefault_path=tmppath) - @_create_testdriver_path - def testDeleteNondefaultOverride(self, tmppath): - """ - Path not selected by default, but we select it, - which will cause it to be manually unlinked - """ - self._testDeleteManyDevices( - nondefault_path=tmppath, - delete_nondefault=True) - assert not os.path.exists(tmppath) +def testDeleteSkipStorage(app): + """ + Test VM delete with all storage skipped + """ + delete = _open_delete(app, "test-many-devices") + chk = delete.find("Delete associated", "check box") + slist = delete.find("storage-list") - @_create_testdriver_path - def testDeleteFailure(self, tmppath): - """ - After launching the wizard we change permissions to make - file deletion fail - """ - paths = self._testDeleteManyDevices( - nondefault_path=tmppath, - delete_nondefault=True, - skip_finish=True) - os.chmod(os.path.dirname(tmppath), 0o555) - delete = self.app.root.find_fuzzy("Delete", "frame") - self._finish(delete, paths, expect_fail=True, click_no=True) - lib.utils.check(lambda: delete.active) - self._finish(delete, paths, expect_fail=True) - assert os.path.exists(tmppath) - self.app.click_alert_button("Errors encountered", "Close") + lib.utils.check(lambda: chk.checked) + chk.click() + lib.utils.check(lambda: not chk.checked) + lib.utils.check(lambda: not slist.showing) - def testDeleteRemoteManyDevices(self): - """ - Test with a remote VM to hit a certain code path - """ - self.app.uri = tests.utils.URIs.kvm_remote - self._testDeleteManyDevices() + _finish(app, delete, None) - def testDeleteSkipStorage(self): - """ - Test VM delete with all storage skipped - """ - delete = self._open_delete("test-many-devices") - chk = delete.find("Delete associated", "check box") - slist = delete.find("storage-list") + # Confirm nothing was deleted compare to the default selections + browser = _open_storage_browser(app) + browser.find_fuzzy("default-pool", "table cell").click() + browser.find("vol-refresh", "push button").click() + app.sleep(.5) + browser.find("overlay.img", "table cell") + browser.find("sharevol.img", "table cell") - lib.utils.check(lambda: chk.checked) - chk.click() - lib.utils.check(lambda: not chk.checked) - lib.utils.check(lambda: not slist.showing) - self._finish(delete, None) +def testDeleteDeviceNoStorage(app): + """ + Verify successful device remove with storage doesn't + touch host storage + """ + details = app.manager_open_details("test-many-devices", + shutdown=True) - # Confirm nothing was deleted compare to the default selections - browser = self._open_storage_browser() - browser.find_fuzzy("default-pool", "table cell").click() - browser.find("vol-refresh", "push button").click() - self.app.sleep(.5) - browser.find("overlay.img", "table cell") - browser.find("sharevol.img", "table cell") + hwlist = details.find("hw-list") + hwlist.click() + c = hwlist.find("USB Disk 1") + c.bring_on_screen() + c.click() + tab = details.find("disk-tab") + lib.utils.check(lambda: tab.showing) + details.find("config-remove").click() - def testDeleteDeviceNoStorage(self): - """ - Verify successful device remove with storage doesn't - touch host storage - """ - details = self.app.open_details_window("test-many-devices", - shutdown=True) + delete = app.find_window("Remove Disk") + chk = delete.find("Delete associated", "check box") + lib.utils.check(lambda: not chk.checked) + _finish(app, delete, []) + details.click() + details.keyCombo("F4") - hwlist = details.find("hw-list") - hwlist.click() - c = hwlist.find("USB Disk 1") - c.bring_on_screen() - c.click() - tab = details.find("disk-tab") - lib.utils.check(lambda: tab.showing) - details.find("config-remove").click() + browser = _open_storage_browser(app) + browser.find_fuzzy("default-pool", "table cell").click() + browser.find("vol-refresh", "push button").click() + app.sleep(.5) + browser.find("overlay.img", "table cell") - delete = self.app.root.find_fuzzy("Remove Disk", "frame") - chk = delete.find("Delete associated", "check box") - lib.utils.check(lambda: not chk.checked) - self._finish(delete, []) - details.click() - details.keyCombo("F4") - browser = self._open_storage_browser() - browser.find_fuzzy("default-pool", "table cell").click() - browser.find("vol-refresh", "push button").click() - self.app.sleep(.5) - browser.find("overlay.img", "table cell") +def testDeleteDeviceWithStorage(app): + """ + Verify successful device remove deletes storage + """ + details = app.manager_open_details("test-many-devices", + shutdown=True) - def testDeleteDeviceWithStorage(self): - """ - Verify successful device remove deletes storage - """ - details = self.app.open_details_window("test-many-devices", - shutdown=True) + hwlist = details.find("hw-list") + hwlist.click() + c = hwlist.find("USB Disk 1") + c.bring_on_screen() + c.click() + tab = details.find("disk-tab") + lib.utils.check(lambda: tab.showing) + details.find("config-remove").click() - hwlist = details.find("hw-list") - hwlist.click() - c = hwlist.find("USB Disk 1") - c.bring_on_screen() - c.click() - tab = details.find("disk-tab") - lib.utils.check(lambda: tab.showing) - details.find("config-remove").click() + delete = app.find_window("Remove Disk") + chk = delete.find("Delete associated", "check box") + lib.utils.check(lambda: not chk.checked) + chk.click() + lib.utils.check(lambda: chk.checked) + path = "/dev/default-pool/overlay.img" + delete.find_fuzzy(path) + _finish(app, delete, [path]) + details.click() + details.keyCombo("F4") - delete = self.app.root.find_fuzzy("Remove Disk", "frame") - chk = delete.find("Delete associated", "check box") - lib.utils.check(lambda: not chk.checked) - chk.click() - lib.utils.check(lambda: chk.checked) - path = "/dev/default-pool/overlay.img" - delete.find_fuzzy(path) - self._finish(delete, [path]) - details.click() - details.keyCombo("F4") + browser = _open_storage_browser(app) + browser.find_fuzzy("default-pool", "table cell").click() + browser.find("vol-refresh", "push button").click() + lib.utils.check(lambda: "overlay.img" not in browser.fmt_nodes()) - browser = self._open_storage_browser() - browser.find_fuzzy("default-pool", "table cell").click() - browser.find("vol-refresh", "push button").click() - lib.utils.check(lambda: "overlay.img" not in browser.fmt_nodes()) - def testDeleteDeviceFail(self): - """ - Verify failed device remove does not touch storage - """ - details = self.app.open_details_window("test-many-devices") +def testDeleteDeviceFail(app): + """ + Verify failed device remove does not touch storage + """ + details = app.manager_open_details("test-many-devices") - hwlist = details.find("hw-list") - hwlist.click() - c = hwlist.find("USB Disk 1") - c.bring_on_screen() - c.click() - tab = details.find("disk-tab") - lib.utils.check(lambda: tab.showing) - details.find("config-remove").click() + hwlist = details.find("hw-list") + hwlist.click() + c = hwlist.find("USB Disk 1") + c.bring_on_screen() + c.click() + tab = details.find("disk-tab") + lib.utils.check(lambda: tab.showing) + details.find("config-remove").click() - delete = self.app.root.find_fuzzy("Remove Disk", "frame") - chk = delete.find("Delete associated", "check box") - lib.utils.check(lambda: not chk.checked) - chk.click() - lib.utils.check(lambda: chk.checked) - path = "/dev/default-pool/overlay.img" - delete.find_fuzzy(path) - self._finish(delete, [path], expect_fail=True) - self.app.click_alert_button("Storage will not be.*deleted", "OK") - details.click() - details.keyCombo("F4") + delete = app.find_window("Remove Disk") + chk = delete.find("Delete associated", "check box") + lib.utils.check(lambda: not chk.checked) + chk.click() + lib.utils.check(lambda: chk.checked) + path = "/dev/default-pool/overlay.img" + delete.find_fuzzy(path) + _finish(app, delete, [path], expect_fail=True) + app.click_alert_button("Storage will not be.*deleted", "OK") + details.click() + details.keyCombo("F4") - # Verify file still exists - browser = self._open_storage_browser() - browser.find_fuzzy("default-pool", "table cell").click() - browser.find("vol-refresh", "push button").click() - self.app.sleep(.5) - browser.find("overlay.img", "table cell") + # Verify file still exists + browser = _open_storage_browser(app) + browser.find_fuzzy("default-pool", "table cell").click() + browser.find("vol-refresh", "push button").click() + app.sleep(.5) + browser.find("overlay.img", "table cell") diff --git a/tests/uitests/test_details.py b/tests/uitests/test_details.py index 87aebdbb..1e80d8fe 100644 --- a/tests/uitests/test_details.py +++ b/tests/uitests/test_details.py @@ -5,887 +5,896 @@ import tests.utils from . import lib -class Details(lib.testcase.UITestCase): - """ - UI tests for virt-manager's VM details window - """ +def _stop_vm(win): + run = win.find("Run", "push button") + win.find("Shut Down", "push button").click() + lib.utils.check(lambda: run.sensitive) - def _select_hw(self, win, hwname, tabname): - c = win.find(hwname, "table cell") - if not c.onscreen: - hwlist = win.find("hw-list") - hwlist.point() - hwlist.click() - self.app.rawinput.keyCombo("f") - searchentry = self.app.root.find(None, "window").find(None, "text") - searchentry.set_text(hwname) - c.check_onscreen() - lib.utils.check(lambda: c.state_selected) - self.app.rawinput.pressKey("Enter") - c.click() - tab = win.find(tabname, None) - lib.utils.check(lambda: tab.showing) - return tab - def _stop_vm(self, win): - run = win.find("Run", "push button") - win.find("Shut Down", "push button").click() - lib.utils.check(lambda: run.sensitive) +def _start_vm(win): + run = win.find("Run", "push button") + run.click() + lib.utils.check(lambda: not run.sensitive) - def _start_vm(self, win): - run = win.find("Run", "push button") - run.click() - lib.utils.check(lambda: not run.sensitive) - - ############## - # Test cases # - ############## - - def _testSmokeTest(self, vmname): - """ - Open the VM with all the crazy hardware and just verify that each - HW panel shows itself without raising any error. - """ - win = self.app.open_details_window(vmname, double=True) - lst = win.find("hw-list", "table") - lib.utils.walkUIList(self.app, win, lst, lambda: False) - - # Select XML editor, and reverse walk the list - win.find("XML", "page tab").click() - lib.utils.walkUIList(self.app, win, lst, lambda: False, reverse=True) - return win - - def testDetailsHardwareSmokeTest(self): - self._testSmokeTest("test-many-devices") - - def testDetailsHardwareSmokeTestAlternate(self): - self.app.open(keyfile="allstats.ini") - win = self._testSmokeTest("test alternate devs title") - win.find("Details", "page tab").click() - self._select_hw(win, "Performance", "performance-tab") - # Wait for perf signals to trigger, to cover more code - self.app.sleep(2) - - def _testRename(self, origname, newname): - # Enable all stats prefs to hit some extra code - win = self.app.open_details_window(origname) - - # Ensure the Overview page is the first selected - win.find("Hypervisor Details", "label") - win.find("Overview", "table cell").click() - - oldcell = self.app.root.find_fuzzy(origname, "table cell") - badname = "foo/bar" - win.find("Name:", "text").set_text(badname) - appl = win.find("config-apply") - appl.click() - self.app.click_alert_button(badname, "Close") - - # Actual name change - win.find("Name:", "text").set_text(newname) - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Confirm lists were updated - self.app.root.find("%s on" % newname, "frame") - self.app.root.find_fuzzy(newname, "table cell") - - # Make sure the old entry is gone - lib.utils.check(lambda: origname not in oldcell.name) - - def testDetailsRenameSimple(self): - """ - Rename a simple VM - """ - self._testRename("test-clone-simple", "test-new-name") - - def testDetailsRenameNVRAM(self): - """ - Rename a VM that will trigger the nvram behavior - """ - origname = "test-many-devices" - # Shutdown the VM - self.app.root.find_fuzzy(origname, "table cell").click() - b = self.app.root.find("Shut Down", "push button") - b.click() - lib.utils.check(lambda: b.sensitive is False) - - self._testRename(origname, "test-new-name") - - def testDetailsStateMisc(self): - """ - Test state changes and unapplied changes warnings - """ - self.app.uri = tests.utils.URIs.kvm - win = self.app.open_details_window("test", shutdown=True) - fmenu = win.find("File", "menu") - fmenu.click() - fmenu.find("View Manager").click() - # Double run to hit a show() codepath - win = self.app.open_details_window("test") - lib.utils.check(lambda: win.active) - appl = win.find("config-apply", "push button") - - # View Manager option - win.find("File", "menu").click() - win.find("View Manager", "menu item").click() - lib.utils.check(lambda: self.app.topwin.active) - self.app.topwin.keyCombo("F4") - lib.utils.check(lambda: win.active) - - # Make a change and then trigger unapplied change warning - tab = self._select_hw(win, "Overview", "overview-tab") - tab.find("Name:", "text").set_text("") - lib.utils.check(lambda: appl.sensitive) - run = win.find("Run", "push button") - run.click() - # Trigger apply error to hit some code paths - self.app.click_alert_button("unapplied changes", "Yes") - self.app.click_alert_button("name must be specified", "Close") - lib.utils.check(lambda: run.sensitive) - consolebtn = win.find("Console", "radio button") - consolebtn.click() - self.app.click_alert_button("unapplied changes", "Yes") - self.app.click_alert_button("name must be specified", "Close") - lib.utils.check(lambda: not consolebtn.checked) - - # Test the pause toggle - win.find("config-cancel").click() - run.click() - lib.utils.check(lambda: not run.sensitive) - pause = win.find("Pause", "toggle button") - pause.click() - lib.utils.check(lambda: pause.checked) - pause.click() - lib.utils.check(lambda: not pause.checked) - lib.utils.check(lambda: win.active) - - def testDetailsEditDomain1(self): - """ - Test overview, memory, cpu pages - """ - self.app.uri = tests.utils.URIs.kvm_cpu_insecure - win = self.app.open_details_window("test") - appl = win.find("config-apply", "push button") - - # Overview description - tab = self._select_hw(win, "Overview", "overview-tab") - tab.find("Description:", "text").set_text("hey new description") - tab.find("Title:", "text").set_text("hey new title") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Memory - tab = self._select_hw(win, "Memory", "memory-tab") - curmem = tab.find("Current allocation:", "spin button") - maxmem = tab.find("Maximum allocation:", "spin button") - curmem.set_text("2000") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - curmem.set_text("50000") - lib.utils.check(lambda: maxmem.text == "50000") - curmem.set_text("5000") - lib.utils.check(lambda: maxmem.text == "50000") - maxmem.set_text("1500") - appl.click() - self.app.click_alert_button("changes will take effect", "OK") - lib.utils.check(lambda: not appl.sensitive) - - # There's no hotplug operations after this point - self._stop_vm(win) - lib.utils.check(lambda: curmem.text == "1500") - lib.utils.check(lambda: maxmem.text == "1500") - - # Static CPU config - # more cpu config: host-passthrough, copy, clear CPU, manual - tab = self._select_hw(win, "CPUs", "cpu-tab") - tab.find("cpu-model").click_combo_entry() - tab.find_fuzzy("Clear CPU", "menu item").click() - appl.click() - lib.utils.check(lambda: not appl.sensitive) - tab.find("cpu-model").click_combo_entry() - tab.find("coreduo", "menu item").click() - appl.click() - lib.utils.check(lambda: not appl.sensitive) - tab.find_fuzzy("CPU security", "check box").click() - appl.click() - lib.utils.check(lambda: not appl.sensitive) - tab.find("cpu-model").click_combo_entry() - tab.find("Application Default", "menu item").click() - appl.click() - lib.utils.check(lambda: not appl.sensitive) - copyhost = tab.find("Copy host", "check box") - lib.utils.check(lambda: copyhost.checked) - copyhost.click() - tab.find("cpu-model").click_combo_entry() - tab.find("Hypervisor Default", "menu item").click() - appl.click() - lib.utils.check(lambda: not appl.sensitive) - tab.find("cpu-model").find(None, "text").text = "host-passthrough" - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # vCPUs - tab.find("vCPU allocation:", "spin button").set_text("50") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # CPU topology - tab.find_fuzzy("Topology", "toggle button").click_expander() - tab.find_fuzzy("Manually set", "check").click() - sockets = tab.find("Sockets:", "spin button") - sockets.typeText("8") - tab.find("Cores:", "spin button").typeText("2") - tab.find("Threads:", "spin button").typeText("2") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - # Confirm VCPUs were adjusted - vcpualloc = tab.find_fuzzy("vCPU allocation", "spin") - lib.utils.check(lambda: vcpualloc.text == "32") - - # Unset topology - tab.find_fuzzy("Manually set", "check").click() - lib.utils.check(lambda: not sockets.sensitive) - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - - def testDetailsEditDomain2(self): - """ - Test boot and OS pages - """ - win = self.app.open_details_window("test-many-devices") - appl = win.find("config-apply", "push button") - self._stop_vm(win) - - - # OS edits - tab = self._select_hw(win, "OS information", "os-tab") - entry = tab.find("oslist-entry") - lib.utils.check(lambda: entry.text == "Fedora") - entry.click() - self.app.rawinput.pressKey("Down") - popover = win.find("oslist-popover") - popover.find("include-eol").click() - entry.set_text("fedora12") - popover.find_fuzzy("fedora12").bring_on_screen().click() - lib.utils.check(lambda: not popover.visible) - lib.utils.check(lambda: entry.text == "Fedora 12") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - lib.utils.check(lambda: entry.text == "Fedora 12") - - - # Boot tweaks - def check_bootorder(c): - # Click the bootlist checkbox, which is hard to find in the tree - x = c.position[0] - 30 - y = c.position[1] + c.size[1] / 2 - button = 1 - self.app.rawinput.click(x, y, button) - - tab = self._select_hw(win, "Boot Options", "boot-tab") - tab.find_fuzzy("Start virtual machine on host", "check box").click() - tab.find("Enable boot menu", "check box").click() - tab.find("SCSI Disk 1", "table cell").click() - tab.find("boot-movedown", "push button").click() - tab.find("Floppy 1", "table cell").click() - tab.find("boot-moveup", "push button").click() - check_bootorder(tab.find("NIC :33:44", "table cell")) - check_bootorder(tab.find("PCI 0003:", "table cell")) - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Kernel boot - tab.find_fuzzy("Direct kernel boot", "toggle button").click_expander() - tab.find_fuzzy("Enable direct kernel", "check box").click() - - tab.find("Kernel args:", "text").set_text("console=ttyS0") - appl.click() - self.app.click_alert_button("arguments without specifying", "OK") - lib.utils.check(lambda: win.active) - - initrd = tab.find("Initrd path:", "text") - tab.find("initrd-browse", "push button").click() - self.app.select_storagebrowser_volume("default-pool", "backingl1.img") - lib.utils.check(lambda: win.active) - lib.utils.check(lambda: "backing" in initrd.text) - appl.click() - self.app.click_alert_button("initrd without specifying", "OK") - lib.utils.check(lambda: win.active) - - tab.find("kernel-browse", "push button").click() - self.app.select_storagebrowser_volume("default-pool", "bochs-vol") - lib.utils.check(lambda: win.active) - kernelpath = tab.find("Kernel path:", "text") - lib.utils.check(lambda: "bochs" in kernelpath.text) - - dtb = tab.find("DTB path:", "text") - tab.find("dtb-browse", "push button").click() - self.app.select_storagebrowser_volume("default-pool", "iso-vol") - lib.utils.check(lambda: win.active) - lib.utils.check(lambda: "iso-vol" in dtb.text) - - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Now disable kernel, but verify that we keep the values in the UI - tab.find_fuzzy("Enable direct kernel", "check box").click() - appl.click() - lib.utils.check(lambda: not appl.sensitive) - tab = self._select_hw(win, "OS information", "os-tab") - tab = self._select_hw(win, "Boot Options", "boot-tab") - lib.utils.check(lambda: "backing" in initrd.text) - - def testDetailsAlternateEdits(self): - """ - Some specific handling via test-alternate-devs - """ - win = self.app.open_details_window("test alternate devs title") - - # tests the console dup removal - self._select_hw(win, "Serial 1", "char-tab") - win.find("config-remove").click() - self.app.click_alert_button("Are you sure", "Yes") - self.app.click_alert_button("take effect after", "OK") - self._stop_vm(win) - - def testDetailsEmptyBoot(self): - """ - Check boot handling when VM has no devices - """ - win = self.app.open_details_window("test-state-crashed") - self._select_hw(win, "Boot Options", "boot-tab") - win.find("No bootable devices") - - # Add in switching back to the console view to hit a vmwindow path - win.find("Console", "radio button").click() - - def testDetailsEditDiskNet(self): - """ - Test disk and network devices - """ - win = self.app.open_details_window("test-many-devices") - appl = win.find("config-apply", "push button") - - # Quick test to hit some serialcon.py paths - viewmenu = win.find("^View$", "menu") - viewmenu.click() - textmenu = viewmenu.find("Consoles", "menu") - textmenu.point() - conitem = textmenu.find("Serial 1") - lib.utils.check(lambda: not conitem.sensitive) - viewmenu.click() - - self._stop_vm(win) - - # Disk options - tab = self._select_hw(win, "IDE Disk 1", "disk-tab") - tab.find("Advanced options", "toggle button").click_expander() - tab.find("Shareable:", "check box").click() - tab.find("Readonly:", "check box").click() - tab.find("Serial:", "text").set_text("1234-ABCD") - tab.combo_select("Cache mode:", "unsafe") - tab.combo_select("Discard mode:", "unmap") - tab.combo_select("Detect zeroes:", "unmap") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Network values w/ macvtap manual - tab = self._select_hw(win, "NIC :54:32:10", "network-tab") - tab.find("IP address", "push button").click() - src = tab.find("net-source") - src.click() - self.app.rawinput.pressKey("Home") - tab.find_fuzzy("Macvtap device...", - "menu item").bring_on_screen().click() - tab.find("Device name:", "text").set_text("fakedev12") - tab.combo_select("Device model:", "rtl8139") - tab.find("Link state:", "check box").click() - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Manual bridge - src.click() - tab.find_fuzzy("Bridge device...", - "menu item").bring_on_screen().click() - tab.find("Device name:", "text").set_text("") - appl.click() - # Check validation error - self.app.click_alert_button("Error changing VM configuration", "Close") - tab.find("Device name:", "text").set_text("zbr0") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - def testDetailsNetIPAddress(self): - """ - Test all the IP code paths with a few mock cases - """ - win = self.app.open_details_window("test-many-devices") - def check_ip(*args): - for ip in args: - tab.find_fuzzy(ip, "label") - - # First case has a virtual network, so hits the leases path - tab = self._select_hw(win, "NIC :54:32:10", "network-tab") - check_ip("10.0.0.2", "fd00:beef::2") - tab.find("IP address:", "push button").click() - check_ip("10.0.0.2", "fd00:beef::2") - - # Next case has a missing virtual network, so hits the arp path - tab = self._select_hw(win, "NIC :11:11:11", "network-tab") - check_ip("Unknown") - tab.find("IP address:", "push button").click() - check_ip("10.0.0.3") - - win.keyCombo("F4") - lib.utils.check(lambda: not win.showing) - self.app.topwin.click_title() - - # Tests the fake qemu guest agent path - win = self.app.open_details_window("test alternate devs title") - tab = self._select_hw(win, "NIC :11:72:72", "network-tab") - check_ip("10.0.0.1", "fd00:beef::1/128") - - - def testDetailsEditDevices1(self): - """ - Test all other devices - """ - win = self.app.open_details_window("test-many-devices") - appl = win.find("config-apply", "push button") - - # Fail to hotremove - tab = self._select_hw(win, "Floppy 1", "disk-tab") - tab.find("Advanced options", "toggle button").click_expander() - share = tab.find("Shareable", "check box") - share.click() - lib.utils.check(lambda: appl.sensitive) - win.find("config-remove").click() - delete = self.app.root.find_fuzzy("Remove Disk", "frame") - delete.find_fuzzy("Delete", "button").click() - self.app.click_alert_button("change will take effect", "OK") - lib.utils.check(lambda: not delete.showing) - lib.utils.check(lambda: appl.sensitive) - lib.utils.check(lambda: share.checked) - win.find("config-cancel").click() - - self._stop_vm(win) - - # Graphics simple VNC -> SPICE - tab = self._select_hw(win, "Display VNC", "graphics-tab") - tab.combo_select("Type:", "Spice") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Spice GL example - tab.combo_select("Listen type:", "None") - tab.find("OpenGL:", "check box").click() - tab.combo_check_default("graphics-rendernode", "0000") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Switch to VNC with options - tab.combo_select("Type:", "VNC") - tab.combo_select("Listen type:", "Address") - tab.find("graphics-port-auto", "check").click() - tab.find("graphics-port-auto", "check").click() - tab.find("graphics-port", "spin button").set_text("6001") - tab.find("Password:", "check").click() - passwd = tab.find_fuzzy("graphics-password", "text") - newpass = "foobar" - passwd.typeText(newpass) - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Sound device - tab = self._select_hw(win, "Sound sb16", "sound-tab") - tab.find("Model:", "text").set_text("ac97") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - # Test non-disk removal - win.find("config-remove").click() - cell = win.find("Sound ac97", "table cell") - oldtext = cell.text - self.app.click_alert_button("Are you sure", "No") - lib.utils.check(lambda: cell.state_selected) - cell.click(button=3) - self.app.root.find("Remove Hardware", "menu item").click() - self.app.click_alert_button("Are you sure", "Yes") - lib.utils.check(lambda: cell.text != oldtext) - - - # Host device - tab = self._select_hw(win, "PCI 0000:00:19.0", "host-tab") - tab.find("ROM BAR:", "check box").click() - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - - # Video device - tab = self._select_hw(win, "Video VMVGA", "video-tab") - tab.find("Model:", "text").set_text("virtio") - tab.find("3D acceleration:", "check box").click() - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - - # Watchdog - tab = self._select_hw(win, "Watchdog", "watchdog-tab") - tab.find("Model:", "text").set_text("diag288") - tab.find("Action:", "text").click() - self.app.rawinput.pressKey("Down") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - - def testDetailsEditDevices2(self): - win = self.app.open_details_window("test-many-devices", - shutdown=True) - appl = win.find("config-apply", "push button") - - # Controller SCSI - tab = self._select_hw( - win, "Controller VirtIO SCSI 9", "controller-tab") - tab.combo_select("controller-model", "Hypervisor") - tab.find("SCSI Disk 1 on 9:0:0:0", "table cell") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # Controller USB - tab = self._select_hw(win, "Controller USB 0", "controller-tab") - tab.combo_select("controller-model", "USB 2") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - tab = self._select_hw(win, "Controller USB 0", "controller-tab") - tab.combo_select("controller-model", "USB 3") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - tab = self._select_hw(win, "Controller USB 0", "controller-tab") - tab.find("controller-model").find(None, "text").text = "piix3-uhci" - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - - # Filesystem tweaks - tab = self._select_hw(win, "Filesystem /target/", "filesystem-tab") - tab.combo_select("Driver:", "Path") - tab.combo_select("Write Policy:", "Immediate") - tab.find("Source path:", "text").set_text("/frib1") - tab.find("Target path:", "text").set_text("newtarget") - tab.find_fuzzy("Export filesystem", "check box").click() - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - - # Smartcard tweaks - tab = self._select_hw(win, "Smartcard", "smartcard-tab") - tab.combo_select("smartcard-mode", "Passthrough") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # TPM tweaks - tab = self._select_hw(win, "TPM", "tpm-tab") - tab.combo_select("tpm-model", "CRB") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - # vsock tweaks - tab = self._select_hw(win, "VirtIO VSOCK", "vsock-tab") - addr = tab.find("vsock-cid") - auto = tab.find("vsock-auto") - lib.utils.check(lambda: addr.text == "5") - addr.set_text("7") - appl.click() - lib.utils.check(lambda: addr.text == "7") - lib.utils.check(lambda: not appl.sensitive) - auto.click() - lib.utils.check(lambda: not addr.visible) - appl.click() - lib.utils.check(lambda: not appl.sensitive) - - - def testDetailsMiscEdits(self): - """ - Test misc editing behavior, like checking for unapplied - changes - """ - win = self.app.open_details_window("test-many-devices", - double=True) +def _select_hw(app, win, hwname, tabname): + c = win.find(hwname, "table cell") + if not c.onscreen: hwlist = win.find("hw-list") + hwlist.point() + hwlist.click() + app.rawinput.keyCombo("f") + searchwin = app.find_window(None, roleName="window") + searchentry = searchwin.find(None, "text") + searchentry.set_text(hwname) + c.check_onscreen() + lib.utils.check(lambda: c.state_selected) + app.rawinput.pressKey("Enter") + c.click() + tab = win.find(tabname, None) + lib.utils.check(lambda: tab.showing) + return tab - # Live device removal, see results after shutdown - disklabel = "SCSI Disk 1" - tab = self._select_hw(win, disklabel, "disk-tab") - win.find("config-remove", "push button").click() - delete = self.app.root.find_fuzzy("Remove Disk", "frame") - delete.find_fuzzy("Delete", "button").click() - # Will be fixed eventually - self.app.click_alert_button("Device could not be removed", "OK") +################################################# +# UI tests for virt-manager's VM details window # +################################################# - c = hwlist.find(disklabel, "table cell") - self._stop_vm(win) - lib.utils.check(lambda: c.text != disklabel) +def _testSmokeTest(app, vmname): + """ + Open the VM with all the crazy hardware and just verify that each + HW panel shows itself without raising any error. + """ + win = app.manager_open_details(vmname) + lst = win.find("hw-list", "table") + lib.utils.walkUIList(app, win, lst, lambda: False) - # Remove a device for offline VM - tab = self._select_hw(win, "SCSI CDROM 1", "disk-tab") - win.find("config-remove", "push button").click() - delete = self.app.root.find_fuzzy("Remove Disk", "frame") - delete.find_fuzzy("Delete", "button").click() - lib.utils.check(lambda: win.active) + # Select XML editor, and reverse walk the list + win.find("XML", "page tab").click() + lib.utils.walkUIList(app, win, lst, lambda: False, reverse=True) + return win - # Attempt to apply changes when skipping away, but they fail - tab.find("Advanced options", "toggle button").click_expander() - tab.find("Cache mode:", "combo").find(None, "text").set_text("badcachemode") - hwlist.find("CPUs", "table cell").click() - self.app.click_alert_button("There are unapplied changes", "Yes") - self.app.click_alert_button("badcachemode", "Close") - # Cancelling changes - tab = self._select_hw(win, "IDE Disk 1", "disk-tab") - share = tab.find("Shareable:", "check box") - lib.utils.check(lambda: not share.checked) - share.click() - win.find("config-cancel").click() - lib.utils.check(lambda: not share.checked) +def testDetailsHardwareSmokeTest(app): + _testSmokeTest(app, "test-many-devices") - # Unapplied, clicking no - share = tab.find("Shareable:", "check box") - share.click() - hwlist.find("CPUs", "table cell").click() - self.app.click_alert_button("There are unapplied changes", "No") - tab = self._select_hw(win, "IDE Disk 1", "disk-tab") - lib.utils.check(lambda: not share.checked) - # Unapplied changes but clicking yes - share.click() - hwlist.find("CPUs", "table cell").click() - alert = self.app.root.find("vmm dialog", "alert") - alert.find_fuzzy("There are unapplied changes", "label") - alert.find_fuzzy("Don't warn", "check box").click() - alert.find("Yes", "push button").click() - tab = self._select_hw(win, "IDE Disk 1", "disk-tab") - lib.utils.check(lambda: share.checked) +def testDetailsHardwareSmokeTestAlternate(app): + app.open(keyfile="allstats.ini") + win = _testSmokeTest(app, "test alternate devs title") + win.find("Details", "page tab").click() + _select_hw(app, win, "Performance", "performance-tab") + # Wait for perf signals to trigger, to cover more code + app.sleep(2) - # Make sure no unapplied changes option sticks - share.click() - self._select_hw(win, "CPUs", "cpu-tab") - tab = self._select_hw(win, "IDE Disk 1", "disk-tab") - lib.utils.check(lambda: share.checked) - # VM State change doesn't refresh UI - share.click() - self._start_vm(win) - lib.utils.check(lambda: not share.checked) +def _testRename(app, win, origname, newname): + # Ensure the Overview page is the first selected + win.find("Hypervisor Details", "label") + win.find("Overview", "table cell").click() - # Now apply changes to running VM, ensure they show up on shutdown + oldcell = app.root.find_fuzzy(origname, "table cell") + badname = "foo/bar" + win.find("Name:", "text").set_text(badname) + appl = win.find("config-apply") + appl.click() + app.click_alert_button(badname, "Close") + + # Actual name change + win.find("Name:", "text").set_text(newname) + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Confirm lists were updated + app.find_window("%s on" % newname) + app.root.find_fuzzy(newname, "table cell") + + # Make sure the old entry is gone + lib.utils.check(lambda: origname not in oldcell.name) + + +def testDetailsRenameSimple(app): + """ + Rename a simple VM + """ + origname = "test-clone-simple" + win = app.manager_open_details(origname) + _testRename(app, win, origname, "test-new-name") + + +def testDetailsRenameNVRAM(app): + """ + Rename a VM that will trigger the nvram behavior + """ + origname = "test-many-devices" + win = app.manager_open_details(origname, shutdown=True) + _testRename(app, win, origname, "test-new-name") + + +def testDetailsStateMisc(app): + """ + Test state changes and unapplied changes warnings + """ + app.uri = tests.utils.URIs.kvm + win = app.manager_open_details("test", shutdown=True) + fmenu = win.find("File", "menu") + fmenu.click() + fmenu.find("View Manager").click() + # Double run to hit a show() codepath + win = app.manager_open_details("test") + lib.utils.check(lambda: win.active) + appl = win.find("config-apply", "push button") + + # View Manager option + win.find("File", "menu").click() + win.find("View Manager", "menu item").click() + lib.utils.check(lambda: app.topwin.active) + app.topwin.keyCombo("F4") + lib.utils.check(lambda: win.active) + + # Make a change and then trigger unapplied change warning + tab = _select_hw(app, win, "Overview", "overview-tab") + tab.find("Name:", "text").set_text("") + lib.utils.check(lambda: appl.sensitive) + run = win.find("Run", "push button") + run.click() + # Trigger apply error to hit some code paths + app.click_alert_button("unapplied changes", "Yes") + app.click_alert_button("name must be specified", "Close") + lib.utils.check(lambda: run.sensitive) + consolebtn = win.find("Console", "radio button") + consolebtn.click() + app.click_alert_button("unapplied changes", "Yes") + app.click_alert_button("name must be specified", "Close") + lib.utils.check(lambda: not consolebtn.checked) + + # Test the pause toggle + win.find("config-cancel").click() + run.click() + lib.utils.check(lambda: not run.sensitive) + pause = win.find("Pause", "toggle button") + pause.click() + lib.utils.check(lambda: pause.checked) + pause.click() + lib.utils.check(lambda: not pause.checked) + lib.utils.check(lambda: win.active) + + +def testDetailsEditDomain1(app): + """ + Test overview, memory, cpu pages + """ + app.uri = tests.utils.URIs.kvm_cpu_insecure + win = app.manager_open_details("test") + appl = win.find("config-apply", "push button") + + # Overview description + tab = _select_hw(app, win, "Overview", "overview-tab") + tab.find("Description:", "text").set_text("hey new description") + tab.find("Title:", "text").set_text("hey new title") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Memory + tab = _select_hw(app, win, "Memory", "memory-tab") + curmem = tab.find("Current allocation:", "spin button") + maxmem = tab.find("Maximum allocation:", "spin button") + curmem.set_text("2000") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + curmem.set_text("50000") + lib.utils.check(lambda: maxmem.text == "50000") + curmem.set_text("5000") + lib.utils.check(lambda: maxmem.text == "50000") + maxmem.set_text("1500") + appl.click() + app.click_alert_button("changes will take effect", "OK") + lib.utils.check(lambda: not appl.sensitive) + + # There's no hotplug operations after this point + _stop_vm(win) + lib.utils.check(lambda: curmem.text == "1500") + lib.utils.check(lambda: maxmem.text == "1500") + + # Static CPU config + # more cpu config: host-passthrough, copy, clear CPU, manual + tab = _select_hw(app, win, "CPUs", "cpu-tab") + tab.find("cpu-model").click_combo_entry() + tab.find_fuzzy("Clear CPU", "menu item").click() + appl.click() + lib.utils.check(lambda: not appl.sensitive) + tab.find("cpu-model").click_combo_entry() + tab.find("coreduo", "menu item").click() + appl.click() + lib.utils.check(lambda: not appl.sensitive) + tab.find_fuzzy("CPU security", "check box").click() + appl.click() + lib.utils.check(lambda: not appl.sensitive) + tab.find("cpu-model").click_combo_entry() + tab.find("Application Default", "menu item").click() + appl.click() + lib.utils.check(lambda: not appl.sensitive) + copyhost = tab.find("Copy host", "check box") + lib.utils.check(lambda: copyhost.checked) + copyhost.click() + tab.find("cpu-model").click_combo_entry() + tab.find("Hypervisor Default", "menu item").click() + appl.click() + lib.utils.check(lambda: not appl.sensitive) + tab.find("cpu-model").find(None, "text").text = "host-passthrough" + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # vCPUs + tab.find("vCPU allocation:", "spin button").set_text("50") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # CPU topology + tab.find_fuzzy("Topology", "toggle button").click_expander() + tab.find_fuzzy("Manually set", "check").click() + sockets = tab.find("Sockets:", "spin button") + sockets.typeText("8") + tab.find("Cores:", "spin button").typeText("2") + tab.find("Threads:", "spin button").typeText("2") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + # Confirm VCPUs were adjusted + vcpualloc = tab.find_fuzzy("vCPU allocation", "spin") + lib.utils.check(lambda: vcpualloc.text == "32") + + # Unset topology + tab.find_fuzzy("Manually set", "check").click() + lib.utils.check(lambda: not sockets.sensitive) + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + + +def testDetailsEditDomain2(app): + """ + Test boot and OS pages + """ + win = app.manager_open_details("test-many-devices") + appl = win.find("config-apply", "push button") + _stop_vm(win) + + + # OS edits + tab = _select_hw(app, win, "OS information", "os-tab") + entry = tab.find("oslist-entry") + lib.utils.check(lambda: entry.text == "Fedora") + entry.click() + app.rawinput.pressKey("Down") + popover = win.find("oslist-popover") + popover.find("include-eol").click() + entry.set_text("fedora12") + popover.find_fuzzy("fedora12").bring_on_screen().click() + lib.utils.check(lambda: not popover.visible) + lib.utils.check(lambda: entry.text == "Fedora 12") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + lib.utils.check(lambda: entry.text == "Fedora 12") + + + # Boot tweaks + def check_bootorder(c): + # Click the bootlist checkbox, which is hard to find in the tree + x = c.position[0] - 30 + y = c.position[1] + c.size[1] / 2 + button = 1 + app.rawinput.click(x, y, button) + + tab = _select_hw(app, win, "Boot Options", "boot-tab") + tab.find_fuzzy("Start virtual machine on host", "check box").click() + tab.find("Enable boot menu", "check box").click() + tab.find("SCSI Disk 1", "table cell").click() + tab.find("boot-movedown", "push button").click() + tab.find("Floppy 1", "table cell").click() + tab.find("boot-moveup", "push button").click() + check_bootorder(tab.find("NIC :33:44", "table cell")) + check_bootorder(tab.find("PCI 0003:", "table cell")) + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Kernel boot + tab.find_fuzzy("Direct kernel boot", "toggle button").click_expander() + tab.find_fuzzy("Enable direct kernel", "check box").click() + + tab.find("Kernel args:", "text").set_text("console=ttyS0") + appl.click() + app.click_alert_button("arguments without specifying", "OK") + lib.utils.check(lambda: win.active) + + initrd = tab.find("Initrd path:", "text") + tab.find("initrd-browse", "push button").click() + app.select_storagebrowser_volume("default-pool", "backingl1.img") + lib.utils.check(lambda: win.active) + lib.utils.check(lambda: "backing" in initrd.text) + appl.click() + app.click_alert_button("initrd without specifying", "OK") + lib.utils.check(lambda: win.active) + + tab.find("kernel-browse", "push button").click() + app.select_storagebrowser_volume("default-pool", "bochs-vol") + lib.utils.check(lambda: win.active) + kernelpath = tab.find("Kernel path:", "text") + lib.utils.check(lambda: "bochs" in kernelpath.text) + + dtb = tab.find("DTB path:", "text") + tab.find("dtb-browse", "push button").click() + app.select_storagebrowser_volume("default-pool", "iso-vol") + lib.utils.check(lambda: win.active) + lib.utils.check(lambda: "iso-vol" in dtb.text) + + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Now disable kernel, but verify that we keep the values in the UI + tab.find_fuzzy("Enable direct kernel", "check box").click() + appl.click() + lib.utils.check(lambda: not appl.sensitive) + tab = _select_hw(app, win, "OS information", "os-tab") + tab = _select_hw(app, win, "Boot Options", "boot-tab") + lib.utils.check(lambda: "backing" in initrd.text) + + +def testDetailsAlternateEdits(app): + """ + Some specific handling via test-alternate-devs + """ + win = app.manager_open_details("test alternate devs title") + + # tests the console dup removal + _select_hw(app, win, "Serial 1", "char-tab") + win.find("config-remove").click() + app.click_alert_button("Are you sure", "Yes") + app.click_alert_button("take effect after", "OK") + _stop_vm(win) + + +def testDetailsEmptyBoot(app): + """ + Check boot handling when VM has no devices + """ + win = app.manager_open_details("test-state-crashed") + _select_hw(app, win, "Boot Options", "boot-tab") + win.find("No bootable devices") + + # Add in switching back to the console view to hit a vmwindow path + win.find("Console", "radio button").click() + + +def testDetailsEditDiskNet(app): + """ + Test disk and network devices + """ + win = app.manager_open_details("test-many-devices") + appl = win.find("config-apply", "push button") + + # Quick test to hit some serialcon.py paths + viewmenu = win.find("^View$", "menu") + viewmenu.click() + textmenu = viewmenu.find("Consoles", "menu") + textmenu.point() + conitem = textmenu.find("Serial 1") + lib.utils.check(lambda: not conitem.sensitive) + viewmenu.click() + + _stop_vm(win) + + # Disk options + tab = _select_hw(app, win, "IDE Disk 1", "disk-tab") + tab.find("Advanced options", "toggle button").click_expander() + tab.find("Shareable:", "check box").click() + tab.find("Readonly:", "check box").click() + tab.find("Serial:", "text").set_text("1234-ABCD") + tab.combo_select("Cache mode:", "unsafe") + tab.combo_select("Discard mode:", "unmap") + tab.combo_select("Detect zeroes:", "unmap") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Network values w/ macvtap manual + tab = _select_hw(app, win, "NIC :54:32:10", "network-tab") + tab.find("IP address", "push button").click() + src = tab.find("net-source") + src.click() + app.rawinput.pressKey("Home") + tab.find_fuzzy("Macvtap device...", + "menu item").bring_on_screen().click() + tab.find("Device name:", "text").set_text("fakedev12") + tab.combo_select("Device model:", "rtl8139") + tab.find("Link state:", "check box").click() + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Manual bridge + src.click() + tab.find_fuzzy("Bridge device...", + "menu item").bring_on_screen().click() + tab.find("Device name:", "text").set_text("") + appl.click() + # Check validation error + app.click_alert_button("Error changing VM configuration", "Close") + tab.find("Device name:", "text").set_text("zbr0") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + +def testDetailsNetIPAddress(app): + """ + Test all the IP code paths with a few mock cases + """ + win = app.manager_open_details("test-many-devices") + def check_ip(*args): + for ip in args: + tab.find_fuzzy(ip, "label") + + # First case has a virtual network, so hits the leases path + tab = _select_hw(app, win, "NIC :54:32:10", "network-tab") + check_ip("10.0.0.2", "fd00:beef::2") + tab.find("IP address:", "push button").click() + check_ip("10.0.0.2", "fd00:beef::2") + + # Next case has a missing virtual network, so hits the arp path + tab = _select_hw(app, win, "NIC :11:11:11", "network-tab") + check_ip("Unknown") + tab.find("IP address:", "push button").click() + check_ip("10.0.0.3") + + win.keyCombo("F4") + lib.utils.check(lambda: not win.showing) + app.topwin.click_title() + + # Tests the fake qemu guest agent path + win = app.manager_open_details("test alternate devs title") + tab = _select_hw(app, win, "NIC :11:72:72", "network-tab") + check_ip("10.0.0.1", "fd00:beef::1/128") + + + +def testDetailsEditDevices1(app): + """ + Test all other devices + """ + win = app.manager_open_details("test-many-devices") + appl = win.find("config-apply", "push button") + + # Fail to hotremove + tab = _select_hw(app, win, "Floppy 1", "disk-tab") + tab.find("Advanced options", "toggle button").click_expander() + share = tab.find("Shareable", "check box") + share.click() + lib.utils.check(lambda: appl.sensitive) + win.find("config-remove").click() + delete = app.find_window("Remove Disk") + delete.find_fuzzy("Delete", "button").click() + app.click_alert_button("change will take effect", "OK") + lib.utils.check(lambda: not delete.showing) + lib.utils.check(lambda: appl.sensitive) + lib.utils.check(lambda: share.checked) + win.find("config-cancel").click() + + _stop_vm(win) + + # Graphics simple VNC -> SPICE + tab = _select_hw(app, win, "Display VNC", "graphics-tab") + tab.combo_select("Type:", "Spice") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Spice GL example + tab.combo_select("Listen type:", "None") + tab.find("OpenGL:", "check box").click() + tab.combo_check_default("graphics-rendernode", "0000") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Switch to VNC with options + tab.combo_select("Type:", "VNC") + tab.combo_select("Listen type:", "Address") + tab.find("graphics-port-auto", "check").click() + tab.find("graphics-port-auto", "check").click() + tab.find("graphics-port", "spin button").set_text("6001") + tab.find("Password:", "check").click() + passwd = tab.find_fuzzy("graphics-password", "text") + newpass = "foobar" + passwd.typeText(newpass) + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Sound device + tab = _select_hw(app, win, "Sound sb16", "sound-tab") + tab.find("Model:", "text").set_text("ac97") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + # Test non-disk removal + win.find("config-remove").click() + cell = win.find("Sound ac97", "table cell") + oldtext = cell.text + app.click_alert_button("Are you sure", "No") + lib.utils.check(lambda: cell.state_selected) + cell.click(button=3) + app.root.find("Remove Hardware", "menu item").click() + app.click_alert_button("Are you sure", "Yes") + lib.utils.check(lambda: cell.text != oldtext) + + + # Host device + tab = _select_hw(app, win, "PCI 0000:00:19.0", "host-tab") + tab.find("ROM BAR:", "check box").click() + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + + # Video device + tab = _select_hw(app, win, "Video VMVGA", "video-tab") + tab.find("Model:", "text").set_text("virtio") + tab.find("3D acceleration:", "check box").click() + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + + # Watchdog + tab = _select_hw(app, win, "Watchdog", "watchdog-tab") + tab.find("Model:", "text").set_text("diag288") + tab.find("Action:", "text").click() + app.rawinput.pressKey("Down") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + + +def testDetailsEditDevices2(app): + win = app.manager_open_details("test-many-devices", + shutdown=True) + appl = win.find("config-apply", "push button") + + # Controller SCSI + tab = _select_hw(app, + win, "Controller VirtIO SCSI 9", "controller-tab") + tab.combo_select("controller-model", "Hypervisor") + tab.find("SCSI Disk 1 on 9:0:0:0", "table cell") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # Controller USB + tab = _select_hw(app, win, "Controller USB 0", "controller-tab") + tab.combo_select("controller-model", "USB 2") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + tab = _select_hw(app, win, "Controller USB 0", "controller-tab") + tab.combo_select("controller-model", "USB 3") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + tab = _select_hw(app, win, "Controller USB 0", "controller-tab") + tab.find("controller-model").find(None, "text").text = "piix3-uhci" + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + + # Filesystem tweaks + tab = _select_hw(app, win, "Filesystem /target/", "filesystem-tab") + tab.combo_select("Driver:", "Path") + tab.combo_select("Write Policy:", "Immediate") + tab.find("Source path:", "text").set_text("/frib1") + tab.find("Target path:", "text").set_text("newtarget") + tab.find_fuzzy("Export filesystem", "check box").click() + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + + # Smartcard tweaks + tab = _select_hw(app, win, "Smartcard", "smartcard-tab") + tab.combo_select("smartcard-mode", "Passthrough") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # TPM tweaks + tab = _select_hw(app, win, "TPM", "tpm-tab") + tab.combo_select("tpm-model", "CRB") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + # vsock tweaks + tab = _select_hw(app, win, "VirtIO VSOCK", "vsock-tab") + addr = tab.find("vsock-cid") + auto = tab.find("vsock-auto") + lib.utils.check(lambda: addr.text == "5") + addr.set_text("7") + appl.click() + lib.utils.check(lambda: addr.text == "7") + lib.utils.check(lambda: not appl.sensitive) + auto.click() + lib.utils.check(lambda: not addr.visible) + appl.click() + lib.utils.check(lambda: not appl.sensitive) + + + +def testDetailsMiscEdits(app): + """ + Test misc editing behavior, like checking for unapplied + changes + """ + win = app.manager_open_details("test-many-devices") + hwlist = win.find("hw-list") + + # Live device removal, see results after shutdown + disklabel = "SCSI Disk 1" + tab = _select_hw(app, win, disklabel, "disk-tab") + win.find("config-remove", "push button").click() + delete = app.find_window("Remove Disk") + delete.find_fuzzy("Delete", "button").click() + + # Will be fixed eventually + app.click_alert_button("Device could not be removed", "OK") + + c = hwlist.find(disklabel, "table cell") + _stop_vm(win) + lib.utils.check(lambda: c.text != disklabel) + + # Remove a device for offline VM + tab = _select_hw(app, win, "SCSI CDROM 1", "disk-tab") + win.find("config-remove", "push button").click() + delete = app.find_window("Remove Disk") + delete.find_fuzzy("Delete", "button").click() + lib.utils.check(lambda: win.active) + + # Attempt to apply changes when skipping away, but they fail + tab.find("Advanced options", "toggle button").click_expander() + tab.find("Cache mode:", "combo").find(None, "text").set_text("badcachemode") + hwlist.find("CPUs", "table cell").click() + app.click_alert_button("There are unapplied changes", "Yes") + app.click_alert_button("badcachemode", "Close") + + # Cancelling changes + tab = _select_hw(app, win, "IDE Disk 1", "disk-tab") + share = tab.find("Shareable:", "check box") + lib.utils.check(lambda: not share.checked) + share.click() + win.find("config-cancel").click() + lib.utils.check(lambda: not share.checked) + + # Unapplied, clicking no + share = tab.find("Shareable:", "check box") + share.click() + hwlist.find("CPUs", "table cell").click() + app.click_alert_button("There are unapplied changes", "No") + tab = _select_hw(app, win, "IDE Disk 1", "disk-tab") + lib.utils.check(lambda: not share.checked) + + # Unapplied changes but clicking yes + share.click() + hwlist.find("CPUs", "table cell").click() + alert = app.root.find("vmm dialog", "alert") + alert.find_fuzzy("There are unapplied changes", "label") + alert.find_fuzzy("Don't warn", "check box").click() + alert.find("Yes", "push button").click() + tab = _select_hw(app, win, "IDE Disk 1", "disk-tab") + lib.utils.check(lambda: share.checked) + + # Make sure no unapplied changes option sticks + share.click() + _select_hw(app, win, "CPUs", "cpu-tab") + tab = _select_hw(app, win, "IDE Disk 1", "disk-tab") + lib.utils.check(lambda: share.checked) + + # VM State change doesn't refresh UI + share.click() + _start_vm(win) + lib.utils.check(lambda: not share.checked) + + # Now apply changes to running VM, ensure they show up on shutdown + win.find("config-apply").click() + app.click_alert_button("changes will take effect", "OK") + lib.utils.check(lambda: share.checked) + _stop_vm(win) + lib.utils.check(lambda: not share.checked) + + # Unapplied changes should warn when switching to XML tab + tab = _select_hw(app, win, "Overview", "overview-tab") + tab.find("Description:", "text").set_text("hey new description") + win.find("XML", "page tab").click() + # Select 'No', meaning don't abandon changes + app.click_alert_button("changes will be lost", "No") + lib.utils.check(lambda: tab.showing) + + # Try unapplied changes again, this time abandon our changes + win.find("XML", "page tab").click() + app.click_alert_button("changes will be lost", "Yes") + lib.utils.check(lambda: not tab.showing) + + # Verify addhardware right click works + win.find("Overview", "table cell").click(button=3) + app.root.find("Add Hardware", "menu item").click() + app.find_window("Add New Virtual Hardware") + + +def testDetailsXMLEdit(app): + """ + Test XML editing interaction + """ + app.open(xmleditor_enabled=True) + win = app.manager_open_details("test-clone-simple") + finish = win.find("config-apply") + xmleditor = win.find("XML editor") + + # Edit vcpu count and verify it's reflected in CPU page + tab = _select_hw(app, win, "CPUs", "cpu-tab") + win.find("XML", "page tab").click() + xmleditor.set_text(xmleditor.text.replace(">5858F4") - lib.utils.check(lambda: win.active) +def testHostStorageVolMisc(app): + """ + Misc actions involving volumes + """ + win = app.manager_open_host("Storage").find("storage-grid") + win.find_fuzzy("default-pool", "table cell").click() + vollist = win.find("vol-list", "table") - # Quit app from the file menu - win.find("File", "menu").click() - win.find("Quit", "menu item").click() - lib.utils.check(lambda: not self.app.is_running()) + vol1 = vollist.find("backingl1.img", "table cell") + vol2 = vollist.find("UPPER", "table cell") + vol1.check_onscreen() + vol2.check_not_onscreen() + win.find("Size", "table column header").click() + win.find("Size", "table column header").click() + vol1.check_not_onscreen() + vol2.check_onscreen() + + vol2.click(button=3) + app.root.find("Copy Volume Path", "menu item").click() + from gi.repository import Gdk, Gtk + clipboard = Gtk.Clipboard.get_default(Gdk.Display.get_default()) + lib.utils.check(lambda: clipboard.wait_for_text() == "/dev/default-pool/UPPER") + + +def testHostConn(app): + """ + Change some connection parameters + """ + manager = app.topwin + # Disconnect the connection + app.manager_conn_disconnect("test testdriver.xml") + + # Open Host Details from right click menu + c = manager.find("test testdriver.xml", "table cell") + c.click(button=3) + app.root.find("conn-details", "menu item").click() + win = app.find_window("test testdriver.xml - Connection Details") + + # Click the tabs and then back + win.find_fuzzy("Storage", "tab").click() + win.find_fuzzy("Network", "tab").click() + win.find_fuzzy("Overview", "tab").click() + + # Toggle autoconnect + win.find("Autoconnect:", "check box").click() + win.find("Autoconnect:", "check box").click() + + # Change the name, verify that title bar changed + win.find("Name:", "text").set_text("FOOBAR") + app.find_window("FOOBAR - Connection Details") + + # Open the manager window + win.find("File", "menu").click() + win.find("View Manager", "menu item").click() + lib.utils.check(lambda: manager.active) + # Confirm connection row is named differently in manager + manager.find("FOOBAR", "table cell") + + # Close the manager + manager.keyCombo("F4") + lib.utils.check(lambda: win.active) + + # Quit app from the file menu + win.find("File", "menu").click() + win.find("Quit", "menu item").click() + lib.utils.check(lambda: not app.is_running()) diff --git a/tests/uitests/test_inspection.py b/tests/uitests/test_inspection.py index 382e9e00..98b159e0 100644 --- a/tests/uitests/test_inspection.py +++ b/tests/uitests/test_inspection.py @@ -13,60 +13,48 @@ except Exception: HAS_LIBGUESTFS = False -class VMMInspection(lib.testcase.UITestCase): - """ - UI tests for the libguestfs inspection infrastructure - """ +######################################################### +# UI tests for the libguestfs inspection infrastructure # +######################################################### - ############## - # Test cases # - ############## +def testInspectionMock(app): + if not HAS_LIBGUESTFS: + pytest.skip("libguestfs python not installed") - def testInspectionMock(self): - if not HAS_LIBGUESTFS: - pytest.skip("libguestfs python not installed") + # Use the test suite inspection mocking to test parts + # of the UI that interact with inspection data + app.open(enable_libguestfs=True) + manager = app.topwin - # Use the test suite inspection mocking to test parts - # of the UI that interact with inspection data - self.app.open(enable_libguestfs=True) - manager = self.app.topwin + details = app.manager_open_details("test-clone") + details.find("OS information", "table cell").click() + tab = details.find("os-tab") - details = self.app.open_details_window("test-clone") - details.find("OS information", "table cell").click() - tab = details.find("os-tab") + tab.find("Application", "toggle").click_expander() + apps = tab.find("inspection-apps") + apps.check_onscreen() + apps.click_expander() - tab.find("Application", "toggle").click_expander() - apps = tab.find("inspection-apps") - apps.check_onscreen() - apps.click_expander() + nodestr1 = apps.fmt_nodes() + assert "test_app1_summary" in nodestr1 + tab.find("Refresh", "push button").click() + lib.utils.check(lambda: apps.fmt_nodes() != nodestr1) - nodestr1 = apps.fmt_nodes() - assert "test_app1_summary" in nodestr1 - tab.find("Refresh", "push button").click() - lib.utils.check(lambda: apps.fmt_nodes() != nodestr1) + details.keyCombo("F4") + lib.utils.check(lambda: not details.showing) - details.keyCombo("F4") - lib.utils.check(lambda: not details.showing) + # Open a VM with no disks which will report an inspection error + app.root.find_fuzzy("test\n", "table cell").doubleClick() + details = app.find_window("test on") + details.find("Details", "radio button").click() + details.find("OS information", "table cell").click() + tab = details.find("os-tab") + tab.find_fuzzy("Fake test error no disks") - # Open a VM with no disks which will report an inspection error - self.app.root.find_fuzzy("test\n", "table cell").doubleClick() - details = self.app.root.find("test on", "frame") - details.find("Details", "radio button").click() - details.find("OS information", "table cell").click() - tab = details.find("os-tab") - tab.find_fuzzy("Fake test error no disks") - - # Closing and reopening a connection triggers some libguest - # cache reading - details.keyCombo("F4") - manager.click() - c = manager.find_fuzzy("testdriver.xml", "table cell") - c.click() - c.click(button=3) - self.app.root.find("conn-disconnect", "menu item").click() - manager.click() - c = manager.find_fuzzy("testdriver.xml", "table cell") - c.click() - c.click(button=3) - self.app.root.find("conn-connect", "menu item").click() - self.app.sleep(2) + # Closing and reopening a connection triggers some libguest + # cache reading + details.keyCombo("F4") + manager.click() + app.manager_conn_disconnect("test testdriver.xml") + app.manager_conn_connect("test testdriver.xml") + app.sleep(2) diff --git a/tests/uitests/test_livetests.py b/tests/uitests/test_livetests.py index 23316a6f..98e68648 100644 --- a/tests/uitests/test_livetests.py +++ b/tests/uitests/test_livetests.py @@ -16,24 +16,24 @@ def _vm_wrapper(vmname, uri="qemu:///system", opts=None): Decorator to define+start a VM and clean it up on exit """ def wrap1(fn): - def wrapper(self, *args, **kwargs): - self.app.error_if_already_running() + def wrapper(app, *args, **kwargs): + app.error_if_already_running() xmlfile = "%s/live/%s.xml" % (tests.utils.UITESTDATADIR, vmname) conn = libvirt.open(uri) dom = conn.defineXML(open(xmlfile).read()) try: dom.create() - self.app.uri = uri - self.conn = conn + app.uri = uri + app.conn = conn extra_opts = (opts or []) extra_opts += ["--show-domain-console", vmname] # Enable stats for more code coverage keyfile = "statsonly.ini" - self.app.open(extra_opts=extra_opts, keyfile=keyfile) - fn(self, dom, *args, **kwargs) + app.open(extra_opts=extra_opts, keyfile=keyfile) + fn(app, dom, *args, **kwargs) finally: try: - self.app.stop() + app.stop() except Exception: pass try: @@ -45,399 +45,404 @@ def _vm_wrapper(vmname, uri="qemu:///system", opts=None): return wrap1 -class Console(lib.testcase.UITestCase): +def _destroy(app, win): + smenu = win.find("Menu", "toggle button") + smenu.click() + smenu.find("Force Off", "menu item").click() + app.click_alert_button("you sure", "Yes") + run = win.find("Run", "push button") + lib.utils.check(lambda: run.sensitive) + + +############################################### +# Test live console connections with stub VMs # +############################################### + +def _checkConsoleStandard(app, dom): """ - Test live console connections with stub VMs + Shared logic for general console handling """ + win = app.topwin + con = win.find("console-gfx-viewport") + lib.utils.check(lambda: con.showing) - conn = None - extraopts = None + win.find("Virtual Machine", "menu").click() + win.find("Take Screenshot", "menu item").click() + chooser = app.root.find(None, "file chooser") + fname = chooser.find("Name", "text").text + app.rawinput.pressKey("Enter") + lib.utils.check(lambda: os.path.exists(fname)) + os.unlink(fname) + lib.utils.check(lambda: win.active) - def _destroy(self, win): - smenu = win.find("Menu", "toggle button") - smenu.click() - smenu.find("Force Off", "menu item").click() - self.app.click_alert_button("you sure", "Yes") - run = win.find("Run", "push button") - lib.utils.check(lambda: run.sensitive) + win.find("Send Key", "menu").click() + win.find(r"Ctrl\+Alt\+F1", "menu item").click() + win.find("Send Key", "menu").click() + win.find(r"Ctrl\+Alt\+F10", "menu item").click() + win.find("Send Key", "menu").click() + win.find(r"Ctrl\+Alt\+Delete", "menu item").click() + + # 'Resize to VM' testing + oldsize = win.size + win.find("^View$", "menu").click() + win.find("Resize to VM", "menu item").click() + newsize = win.size + lib.utils.check(lambda: oldsize != newsize) + + # Fullscreen testing + win.find("^View$", "menu").click() + win.find("Fullscreen", "check menu item").click() + fstb = win.find("Fullscreen Toolbar") + lib.utils.check(lambda: fstb.showing) + lib.utils.check(lambda: win.size != newsize) + + # Wait for toolbar to hide, then reveal it again + lib.utils.check(lambda: not fstb.showing, timeout=5) + app.rawinput.point(win.position[0] + win.size[0] / 2, 0) + lib.utils.check(lambda: fstb.showing) + # Move it off and have it hide again + win.point() + lib.utils.check(lambda: not fstb.showing, timeout=5) + app.rawinput.point(win.position[0] + win.size[0] / 2, 0) + lib.utils.check(lambda: fstb.showing) + + # Click stuff and exit fullscreen + win.find("Fullscreen Send Key").click() + app.rawinput.pressKey("Escape") + win.find("Fullscreen Exit").click() + lib.utils.check(lambda: win.size == newsize) + + # Trigger pointer grab, verify title was updated + win.click() + lib.utils.check(lambda: "Control_L" in win.name) + # Ungrab + win.keyCombo("") + lib.utils.check(lambda: "Control_L" not in win.name) + + # Tweak scaling + win.window_maximize() + 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() + win.find("^View$", "menu").click() + scalemenu = win.find("Scale Display", "menu") + scalemenu.point() + scalemenu.find("Only", "radio menu item").click() + + # Check that modifiers don't work + win.click() + app.sleep(1) + win.keyCombo("w") + lib.utils.check(lambda: win.showing) + dom.destroy() + win.find("Guest is not running.") + win.click_title() + app.sleep(1) + win.keyCombo("w") + lib.utils.check(lambda: not win.showing) - ############## - # Test cases # - ############## +@_vm_wrapper("uitests-vnc-standard") +def testConsoleVNCStandard(app, dom): + return _checkConsoleStandard(app, dom) - def _checkConsoleStandard(self, dom): - """ - Shared logic for general console handling - """ - win = self.app.topwin - con = win.find("console-gfx-viewport") - lib.utils.check(lambda: con.showing) - win.find("Virtual Machine", "menu").click() - win.find("Take Screenshot", "menu item").click() - chooser = self.app.root.find(None, "file chooser") - fname = chooser.find("Name", "text").text - self.app.rawinput.pressKey("Enter") - lib.utils.check(lambda: os.path.exists(fname)) - os.unlink(fname) - lib.utils.check(lambda: win.active) +@_vm_wrapper("uitests-spice-standard") +def testConsoleSpiceStandard(app, dom): + return _checkConsoleStandard(app, dom) - win.find("Send Key", "menu").click() - win.find(r"Ctrl\+Alt\+F1", "menu item").click() - win.find("Send Key", "menu").click() - win.find(r"Ctrl\+Alt\+F10", "menu item").click() - win.find("Send Key", "menu").click() - win.find(r"Ctrl\+Alt\+Delete", "menu item").click() - # 'Resize to VM' testing - oldsize = win.size - win.find("^View$", "menu").click() - win.find("Resize to VM", "menu item").click() - newsize = win.size - lib.utils.check(lambda: oldsize != newsize) +def _checkPassword(app): + """ + Shared logic for password handling + """ + win = app.topwin + con = win.find("console-gfx-viewport") + lib.utils.check(lambda: not con.showing) + passwd = win.find("Password:", "password text") + lib.utils.check(lambda: passwd.showing) - # Fullscreen testing - win.find("^View$", "menu").click() - win.find("Fullscreen", "check menu item").click() - fstb = win.find("Fullscreen Toolbar") - lib.utils.check(lambda: fstb.showing) - lib.utils.check(lambda: win.size != newsize) - - # Wait for toolbar to hide, then reveal it again - lib.utils.check(lambda: not fstb.showing, timeout=5) - self.app.rawinput.point(win.position[0] + win.size[0] / 2, 0) - lib.utils.check(lambda: fstb.showing) - # Move it off and have it hide again - win.point() - lib.utils.check(lambda: not fstb.showing, timeout=5) - self.app.rawinput.point(win.position[0] + win.size[0] / 2, 0) - lib.utils.check(lambda: fstb.showing) - - # Click stuff and exit fullscreen - win.find("Fullscreen Send Key").click() - self.app.rawinput.pressKey("Escape") - win.find("Fullscreen Exit").click() - lib.utils.check(lambda: win.size == newsize) - - # Trigger pointer grab, verify title was updated - win.click() - lib.utils.check(lambda: "Control_L" in win.name) - # Ungrab - win.keyCombo("") - lib.utils.check(lambda: "Control_L" not in win.name) - - # 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() - win.find("^View$", "menu").click() - scalemenu = win.find("Scale Display", "menu") - scalemenu.point() - scalemenu.find("Only", "radio menu item").click() - - # Check that modifiers don't work - win.click() - self.app.sleep(1) - win.keyCombo("w") - lib.utils.check(lambda: win.showing) - dom.destroy() - win.find("Guest is not running.") - win.click_title() - self.app.sleep(1) - win.keyCombo("w") - lib.utils.check(lambda: not win.showing) - - @_vm_wrapper("uitests-vnc-standard") - def testConsoleVNCStandard(self, dom): - return self._checkConsoleStandard(dom) - @_vm_wrapper("uitests-spice-standard") - def testConsoleSpiceStandard(self, dom): - return self._checkConsoleStandard(dom) - - def _checkPassword(self): - """ - Shared logic for password handling - """ - win = self.app.topwin - con = win.find("console-gfx-viewport") - lib.utils.check(lambda: not con.showing) - passwd = win.find("Password:", "password text") - lib.utils.check(lambda: passwd.showing) - - # Check wrong password handling - passwd.typeText("xx") - win.find("Login", "push button").click() - self.app.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.app.rawinput.pressKey("Enter") - self.app.click_alert_button("Viewer authentication error", "OK") - - # Check proper password - passwd.text = "" - passwd.typeText("goodp") - win.find("Login", "push button").click() - lib.utils.check(lambda: con.showing) - - # Restart VM to retrigger console connect - self._destroy(win) - win.find("Run", "push button").click() - lib.utils.check(lambda: passwd.showing) - # Password should be filled in - lib.utils.check(lambda: bool(passwd.text)) - # Uncheck 'Save password' and login, which will delete it from keyring + # Check wrong password handling + passwd.typeText("xx") + win.find("Login", "push button").click() + app.click_alert_button("Viewer authentication error", "OK") + savecheck = win.find("Save this password", "check box") + if not savecheck.checked: savecheck.click() - win.find("Login", "push button").click() - lib.utils.check(lambda: con.showing) + passwd.typeText("yy") + app.rawinput.pressKey("Enter") + app.click_alert_button("Viewer authentication error", "OK") - # Restart VM to retrigger console connect - self._destroy(win) - win.find("Run", "push button").click() - lib.utils.check(lambda: passwd.showing) - # Password should be empty now - lib.utils.check(lambda: not bool(passwd.text)) + # Check proper password + passwd.text = "" + passwd.typeText("goodp") + win.find("Login", "push button").click() + lib.utils.check(lambda: con.showing) - @_vm_wrapper("uitests-vnc-password") - def testConsoleVNCPassword(self, dom): - ignore = dom - return self._checkPassword() - @_vm_wrapper("uitests-spice-password") - def testConsoleSpicePassword(self, dom): - ignore = dom - return self._checkPassword() + # Restart VM to retrigger console connect + _destroy(app, win) + win.find("Run", "push button").click() + lib.utils.check(lambda: passwd.showing) + # Password should be filled in + lib.utils.check(lambda: bool(passwd.text)) + # Uncheck 'Save password' and login, which will delete it from keyring + savecheck.click() + win.find("Login", "push button").click() + lib.utils.check(lambda: con.showing) - @_vm_wrapper("uitests-vnc-password", - opts=["--test-options=fake-vnc-username"]) - def testConsoleVNCPasswordUsername(self, dom): - ignore = dom - win = self.app.topwin - con = win.find("console-gfx-viewport") - lib.utils.check(lambda: not con.showing) - passwd = win.find("Password:", "password text") - lib.utils.check(lambda: passwd.showing) - username = win.find("Username:", "text") - lib.utils.check(lambda: username.showing) + # Restart VM to retrigger console connect + _destroy(app, win) + win.find("Run", "push button").click() + lib.utils.check(lambda: passwd.showing) + # Password should be empty now + lib.utils.check(lambda: not bool(passwd.text)) - # Since we are mocking the username, sending the credentials - # is ignored, so with the correct password this succeeds - username.text = "fakeuser" - passwd.typeText("goodp") - win.find("Login", "push button").click() - lib.utils.check(lambda: con.showing) - @_vm_wrapper("uitests-vnc-socket") - def testConsoleVNCSocket(self, dom): - ignore = dom - win = self.app.topwin - con = win.find("console-gfx-viewport") - lib.utils.check(lambda: con.showing) +@_vm_wrapper("uitests-vnc-password") +def testConsoleVNCPassword(app, dom): + ignore = dom + return _checkPassword(app) - def _click_textconsole_menu(msg): - vmenu = win.find("^View$", "menu") - vmenu.click() - tmenu = win.find("Consoles", "menu") - tmenu.point() - tmenu.find(msg, "radio menu item").click() - # A bit of an extra test, make sure selecting Graphical Console works - _click_textconsole_menu("Serial 1") - lib.utils.check(lambda: not con.showing) - _click_textconsole_menu("Graphical Console") - lib.utils.check(lambda: con.showing) +@_vm_wrapper("uitests-spice-password") +def testConsoleSpicePassword(app, dom): + ignore = dom + return _checkPassword(app) - @_vm_wrapper("uitests-spice-standard") - def testConsoleAutoconnect(self, dom): - ignore = dom - win = self.app.topwin - con = win.find("console-gfx-viewport") - lib.utils.check(lambda: con.showing) - # Disable autoconnect +@_vm_wrapper("uitests-vnc-password", + opts=["--test-options=fake-vnc-username"]) +def testConsoleVNCPasswordUsername(app, dom): + ignore = dom + win = app.topwin + con = win.find("console-gfx-viewport") + lib.utils.check(lambda: not con.showing) + passwd = win.find("Password:", "password text") + lib.utils.check(lambda: passwd.showing) + username = win.find("Username:", "text") + lib.utils.check(lambda: username.showing) + + # Since we are mocking the username, sending the credentials + # is ignored, so with the correct password this succeeds + username.text = "fakeuser" + passwd.typeText("goodp") + win.find("Login", "push button").click() + lib.utils.check(lambda: con.showing) + + +@_vm_wrapper("uitests-vnc-socket") +def testConsoleVNCSocket(app, dom): + ignore = dom + win = app.topwin + con = win.find("console-gfx-viewport") + lib.utils.check(lambda: con.showing) + + def _click_textconsole_menu(msg): vmenu = win.find("^View$", "menu") vmenu.click() - vmenu.find("Autoconnect").click() - dom.destroy() - self.app.sleep(1) - dom.create() - lib.utils.check(lambda: not con.showing) - win.find("Connect to console", "push button").click() - lib.utils.check(lambda: con.showing) + tmenu = win.find("Consoles", "menu") + tmenu.point() + # We need to sleep to give the menu time to dynamically populate + app.sleep(.5) + tmenu.find(msg, "radio menu item").click() - @_vm_wrapper("uitests-lxc-serial", uri="lxc:///") - def testConsoleLXCSerial(self, dom): - """ - Ensure LXC has serial open, and we can send some data - """ - ignore = dom - win = self.app.topwin - term = win.find("Serial Terminal") - lib.utils.check(lambda: term.showing) - term.typeText("help\n") - lib.utils.check(lambda: "COMMANDS" in term.text) - - term.doubleClick() - term.click(button=3) - menu = self.app.root.find("serial-popup-menu") - menu.find("Copy", "menu item").click() - - term.click() - term.click(button=3) - menu = self.app.root.find("serial-popup-menu") - menu.find("Paste", "menu item").click() - - win.find("Details", "radio button").click() - win.find("Console", "radio button").click() - self._destroy(win) - view = self.app.root.find("^View$", "menu") - view.click() - # Triggers some tooltip cases - textmenu = view.find("Consoles", "menu") - textmenu.point() - lib.utils.check(lambda: textmenu.showing) - item = textmenu.find("Text Console 1") - lib.utils.check(lambda: not item.sensitive) - - # Restart the guest to trigger reconnect code - view.click() - win.find("Run", "push button").click() - term = win.find("Serial Terminal") - lib.utils.check(lambda: term.showing) - - # Ensure ctrl+w doesn't close the window, modifiers are disabled - term.click() - win.keyCombo("w") - lib.utils.check(lambda: win.showing) - # Shut it down, ensure w works again - self._destroy(win) - win.click_title() - self.app.sleep(1) - win.keyCombo("w") - lib.utils.check(lambda: not win.showing) + # A bit of an extra test, make sure selecting Graphical Console works + _click_textconsole_menu("Serial 1") + lib.utils.check(lambda: not con.showing) + _click_textconsole_menu("Graphical Console") + lib.utils.check(lambda: con.showing) - @_vm_wrapper("uitests-spice-specific", - opts=["--test-options=spice-agent", - "--test-options=fake-console-resolution"]) - def testConsoleSpiceSpecific(self, dom): - """ - Spice specific behavior. Has lots of devices that will open - channels, spice GL + local config, and usbredir - """ - ignore = dom - win = self.app.topwin - con = win.find("console-gfx-viewport") - lib.utils.check(lambda: con.showing) +@_vm_wrapper("uitests-spice-standard") +def testConsoleAutoconnect(app, dom): + ignore = dom + win = app.topwin + con = win.find("console-gfx-viewport") + lib.utils.check(lambda: con.showing) - # Just ensure the dialog pops up, can't really test much more - # than that - win.find("Virtual Machine", "menu").click() - win.find("Redirect USB", "menu item").click() - - 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.app.rawinput.pressKey("Enter") - lib.utils.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): - win = self.app.topwin - win.find("Details", "radio button").click() - - # Add a scsi disk, importing the passed path - win.find("add-hardware", "push button").click() - addhw = self.app.root.find("Add New Virtual Hardware", "frame") - addhw.find("Storage", "table cell").click() - tab = addhw.find("storage-tab", None) - lib.utils.check(lambda: tab.showing) - tab.find("Select or create", "radio button").click() - tab.find("storage-entry").set_text(fname) - tab.combo_select("Bus type:", "SCSI") - addhw.find("Finish", "push button").click() - - # Verify permission dialog pops up, ask to change - self.app.click_alert_button( - "The emulator may not have search permissions", "Yes") - - # Verify no errors - lib.utils.check(lambda: not addhw.showing) - lib.utils.check(lambda: win.active) - - # Hot unplug the disk - win.find("SCSI Disk 1", "table cell").click() - tab = win.find("disk-tab", None) - lib.utils.check(lambda: tab.showing) - win.find("config-remove").click() - delete = self.app.root.find_fuzzy("Remove Disk", "frame") - delete.find_fuzzy("Delete", "button").click() - lib.utils.check(lambda: not delete.active) - lib.utils.check(lambda: os.path.exists(fname)) - - # Change CDROM - win.find("IDE CDROM 1", "table cell").click() - tab = win.find("disk-tab", None) - entry = win.find("media-entry") - appl = win.find("config-apply") - lib.utils.check(lambda: tab.showing) - entry.set_text(fname) - appl.click() - lib.utils.check(lambda: not appl.sensitive) - lib.utils.check(lambda: entry.text == fname) - entry.click_secondary_icon() - appl.click() - lib.utils.check(lambda: not appl.sensitive) - lib.utils.check(lambda: not entry.text) + # Disable autoconnect + vmenu = win.find("^View$", "menu") + vmenu.click() + vmenu.find("Autoconnect").click() + dom.destroy() + app.sleep(1) + dom.create() + lib.utils.check(lambda: not con.showing) + win.find("Connect to console", "push button").click() + lib.utils.check(lambda: con.showing) - @_vm_wrapper("uitests-hotplug") - def testLiveHotplug(self, dom): - """ - Live test for basic hotplugging and media change, as well as - testing our auto-poolify magic - """ - ignore = dom - import tempfile - tmpdir = tempfile.TemporaryDirectory(prefix="uitests-tmp") - dname = tmpdir.name +@_vm_wrapper("uitests-lxc-serial", uri="lxc:///") +def testConsoleLXCSerial(app, dom): + """ + Ensure LXC has serial open, and we can send some data + """ + ignore = dom + win = app.topwin + term = win.find("Serial Terminal") + lib.utils.check(lambda: term.showing) + term.typeText("help\n") + lib.utils.check(lambda: "COMMANDS" in term.text) + + term.doubleClick() + term.click(button=3) + menu = app.root.find("serial-popup-menu") + menu.find("Copy", "menu item").click() + + term.click() + term.click(button=3) + menu = app.root.find("serial-popup-menu") + menu.find("Paste", "menu item").click() + + win.find("Details", "radio button").click() + win.find("Console", "radio button").click() + _destroy(app, win) + view = app.root.find("^View$", "menu") + view.click() + # Triggers some tooltip cases + textmenu = view.find("Consoles", "menu") + textmenu.point() + lib.utils.check(lambda: textmenu.showing) + app.sleep(.5) + item = textmenu.find("Text Console 1") + lib.utils.check(lambda: not item.sensitive) + + # Restart the guest to trigger reconnect code + view.click() + win.find("Run", "push button").click() + term = win.find("Serial Terminal") + lib.utils.check(lambda: term.showing) + + # Ensure ctrl+w doesn't close the window, modifiers are disabled + term.click() + win.keyCombo("w") + lib.utils.check(lambda: win.showing) + # Shut it down, ensure w works again + _destroy(app, win) + win.click_title() + app.sleep(1) + win.keyCombo("w") + lib.utils.check(lambda: not win.showing) + + +@_vm_wrapper("uitests-spice-specific", + opts=["--test-options=spice-agent", + "--test-options=fake-console-resolution"]) +def testConsoleSpiceSpecific(app, dom): + """ + Spice specific behavior. Has lots of devices that will open + channels, spice GL + local config, and usbredir + """ + ignore = dom + win = app.topwin + con = win.find("console-gfx-viewport") + lib.utils.check(lambda: con.showing) + + # Just ensure the dialog pops up, can't really test much more + # than that + win.find("Virtual Machine", "menu").click() + win.find("Redirect USB", "menu item").click() + + usbwin = app.root.find("vmm dialog", "alert") + usbwin.find("Select USB devices for redirection", "label") + usbwin.find("SPICE CD", "check box").click() + chooser = 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() + app.rawinput.pressKey("Enter") + lib.utils.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.window_maximize() + _click_auto() + win.click_title() + win.click_title() + + +def _testLiveHotplug(app, fname): + win = app.topwin + win.find("Details", "radio button").click() + + # Add a scsi disk, importing the passed path + win.find("add-hardware", "push button").click() + addhw = app.find_window("Add New Virtual Hardware") + addhw.find("Storage", "table cell").click() + tab = addhw.find("storage-tab", None) + lib.utils.check(lambda: tab.showing) + tab.find("Select or create", "radio button").click() + tab.find("storage-entry").set_text(fname) + tab.combo_select("Bus type:", "SCSI") + addhw.find("Finish", "push button").click() + + # Verify permission dialog pops up, ask to change + app.click_alert_button( + "The emulator may not have search permissions", "Yes") + + # Verify no errors + lib.utils.check(lambda: not addhw.showing) + lib.utils.check(lambda: win.active) + + # Hot unplug the disk + win.find("SCSI Disk 1", "table cell").click() + tab = win.find("disk-tab", None) + lib.utils.check(lambda: tab.showing) + win.find("config-remove").click() + delete = app.find_window("Remove Disk") + delete.find_fuzzy("Delete", "button").click() + lib.utils.check(lambda: not delete.active) + lib.utils.check(lambda: os.path.exists(fname)) + + # Change CDROM + win.find("IDE CDROM 1", "table cell").click() + tab = win.find("disk-tab", None) + entry = win.find("media-entry") + appl = win.find("config-apply") + lib.utils.check(lambda: tab.showing) + entry.set_text(fname) + appl.click() + lib.utils.check(lambda: not appl.sensitive) + lib.utils.check(lambda: entry.text == fname) + entry.click_secondary_icon() + appl.click() + lib.utils.check(lambda: not appl.sensitive) + lib.utils.check(lambda: not entry.text) + + +@_vm_wrapper("uitests-hotplug") +def testLiveHotplug(app, dom): + """ + Live test for basic hotplugging and media change, as well as + testing our auto-poolify magic + """ + ignore = dom + import tempfile + tmpdir = tempfile.TemporaryDirectory(prefix="uitests-tmp") + dname = tmpdir.name + try: + fname = os.path.join(dname, "test.img") + os.system("qemu-img create -f qcow2 %s 1M > /dev/null" % fname) + os.system("chmod 700 %s" % dname) + _testLiveHotplug(app, fname) + finally: + poolname = os.path.basename(dname) try: - fname = os.path.join(dname, "test.img") - os.system("qemu-img create -f qcow2 %s 1M > /dev/null" % fname) - os.system("chmod 700 %s" % dname) - self._testLiveHotplug(fname) - finally: - poolname = os.path.basename(dname) - try: - pool = self.conn.storagePoolLookupByName(poolname) - pool.destroy() - pool.undefine() - except Exception: - log.debug("Error cleaning up pool", exc_info=True) + pool = app.conn.storagePoolLookupByName(poolname) + pool.destroy() + pool.undefine() + except Exception: + log.debug("Error cleaning up pool", exc_info=True) diff --git a/tests/uitests/test_manager.py b/tests/uitests/test_manager.py index e7b9dfa7..079dfa55 100644 --- a/tests/uitests/test_manager.py +++ b/tests/uitests/test_manager.py @@ -5,358 +5,331 @@ import tests.utils from . import lib -class Manager(lib.testcase.UITestCase): +############################################################# +# UI tests for manager window, and basic VM lifecycle stuff # +############################################################# + +def _testVMLifecycle(app): """ - UI tests for manager window, and basic VM lifecycle stuff + Basic VM lifecycle test, shared between standard and no-events + testing """ + manager = app.topwin + shutdown = manager.find("Shut Down", "push button") + pause = manager.find("Pause", "toggle button") + run = manager.find("Run", "push button") + force = manager.find("Force Off", "menu item") + smenu = manager.find("Menu", "toggle button") + save = manager.find("Save", "menu item") - ############## - # Test cases # - ############## + c = manager.find("test-many-devices", "table cell") + c.click() + smenu.click() + force.click() + app.click_alert_button("Are you sure you want", "Yes") + lib.utils.check(lambda: run.sensitive, timeout=5) - def _testVMLifecycle(self): - """ - Basic VM lifecycle test, shared between standard and no-events - testing - """ - manager = self.app.topwin - shutdown = manager.find("Shut Down", "push button") - pause = manager.find("Pause", "toggle button") - run = manager.find("Run", "push button") - force = manager.find("Force Off", "menu item") - smenu = manager.find("Menu", "toggle button") - save = manager.find("Save", "menu item") - - c = manager.find("test-many-devices", "table cell") - c.click() - smenu.click() - force.click() - self.app.click_alert_button("Are you sure you want", "Yes") - lib.utils.check(lambda: run.sensitive, timeout=5) - - run.click() - lib.utils.check(lambda: not run.sensitive, timeout=5) - pause.click() - lib.utils.check(lambda: pause.checked, timeout=5) - pause.click() - lib.utils.check(lambda: not pause.checked, timeout=5) - smenu.click() - save.click() - lib.utils.check(lambda: run.sensitive, timeout=5) - lib.utils.check(lambda: "Saved" in c.text) - run.click() - lib.utils.check(lambda: shutdown.sensitive, timeout=5) - - def testVMLifecycle(self): - # qemu hits some different domain code paths for setTime - self.app.uri = tests.utils.URIs.kvm - self._testVMLifecycle() - - def testVMNoEventsLifecycle(self): - self.app.open(extra_opts=["--test-options=no-events"]) - - # Change preferences timeout to 1 second - self.app.root.find("Edit", "menu").click() - self.app.root.find("Preferences", "menu item").click() - win = self.app.root.find_fuzzy("Preferences", "frame") - win.find("Polling", "page tab").click() - win.find("cpu-poll").set_text("1") - win.find("Close", "push button").click() - - self._testVMLifecycle() - - def testVMLifecycleExtra(self): - """ - Test vmmenu lifecycle options - """ - self.app.open(keyfile="confirm-all.ini") - manager = self.app.topwin - run = manager.find("Run", "push button") - shutdown = manager.find("Shut Down", "push button") - pause = manager.find("Pause", "toggle button") - - def confirm_is_running(): - lib.utils.check(lambda: not run.sensitive) - - def confirm_is_shutdown(): - lib.utils.check(lambda: not shutdown.sensitive) - - def confirm_is_paused(): - lib.utils.check(lambda: pause.checked) - - def confirm_not_paused(): - lib.utils.check(lambda: not pause.checked) - - def test_action(action, shutdown=True, confirm=True): - def _select(): - cell = manager.find("test\n", "table cell") - cell.click(button=3) - menu = self.app.root.find("vm-action-menu") - menu.check_onscreen() - if shutdown: - smenu = menu.find("Shut Down", "menu") - smenu.point() - smenu.check_onscreen() - item = smenu.find(action, "menu item") - else: - item = menu.find(action, "menu item") - item.check_onscreen() - item.point() - self.app.sleep(.3) - item.click() - - _select() - if confirm: - self.app.click_alert_button("Are you sure", "No") - _select() - self.app.click_alert_button("Are you sure", "Yes") + run.click() + lib.utils.check(lambda: not run.sensitive, timeout=5) + pause.click() + lib.utils.check(lambda: pause.checked, timeout=5) + pause.click() + lib.utils.check(lambda: not pause.checked, timeout=5) + smenu.click() + save.click() + lib.utils.check(lambda: run.sensitive, timeout=5) + lib.utils.check(lambda: "Saved" in c.text) + run.click() + lib.utils.check(lambda: shutdown.sensitive, timeout=5) - test_action("Force Reset") - confirm_is_running() - test_action("Reboot") - confirm_is_running() - test_action("Shut Down") - confirm_is_shutdown() - test_action("Run", shutdown=False, confirm=False) - confirm_is_running() - test_action("Force Off") - confirm_is_shutdown() - test_action("Run", shutdown=False, confirm=False) - confirm_is_running() - test_action("Pause", shutdown=False) - confirm_is_paused() - test_action("Resume", shutdown=False, confirm=False) - confirm_not_paused() - test_action("Save") - confirm_is_shutdown() - test_action("Restore", shutdown=False, confirm=False) - confirm_is_running() +def testVMLifecycle(app): + # qemu hits some different domain code paths for setTime + app.uri = tests.utils.URIs.kvm + _testVMLifecycle(app) - def testManagerSaveCancelError(self): - """ - Test managed save special behavior - """ - self.app.open(extra_opts=["--test-options=test-managed-save"]) - manager = self.app.topwin - run = manager.find("Run", "push button") - smenu = manager.find("Menu", "toggle button") - save = manager.find("Save", "menu item") +def testVMNoEventsLifecycle(app): + app.open(extra_opts=["--test-options=no-events"]) - c = manager.find("test-many-devices", "table cell") - c.click() + # Change preferences timeout to 1 second + app.root.find("Edit", "menu").click() + app.root.find("Preferences", "menu item").click() + win = app.find_window("Preferences") + win.find("Polling", "page tab").click() + win.find("cpu-poll").set_text("1") + win.find("Close", "push button").click() - # Save it, attempt a cancel operation - smenu.click() - save.click() - progwin = self.app.root.find("Saving Virtual Machine", "frame") - # Attempt cancel which will fail, then find the error message - progwin.find("Cancel", "push button").click() - progwin.find("Error cancelling save job") - lib.utils.check(lambda: not progwin.showing, timeout=5) - lib.utils.check(lambda: run.sensitive) + _testVMLifecycle(app) - # Restore will fail and offer to remove managed save - run.click() - self.app.click_alert_button("remove the saved state", "No") - lib.utils.check(lambda: run.sensitive) - run.click() - self.app.click_alert_button("remove the saved state", "Yes") + +def testVMLifecycleExtra(app): + """ + Test vmmenu lifecycle options + """ + app.open(keyfile="confirm-all.ini") + manager = app.topwin + run = manager.find("Run", "push button") + shutdown = manager.find("Shut Down", "push button") + pause = manager.find("Pause", "toggle button") + + def confirm_is_running(): lib.utils.check(lambda: not run.sensitive) - def testManagerQEMUSetTime(self): + def confirm_is_shutdown(): + lib.utils.check(lambda: not shutdown.sensitive) + + def confirm_is_paused(): + lib.utils.check(lambda: pause.checked) + + def confirm_not_paused(): + lib.utils.check(lambda: not pause.checked) + + def test_action(**kwargs): + app.manager_vm_action("test", confirm_click_no=True, **kwargs) + + confirm_is_running() + test_action(reset=True) + confirm_is_running() + test_action(reboot=True) + confirm_is_running() + test_action(shutdown=True) + confirm_is_shutdown() + test_action(run=True) + confirm_is_running() + test_action(destroy=True) + confirm_is_shutdown() + test_action(run=True) + confirm_is_running() + test_action(pause=True) + confirm_is_paused() + test_action(resume=True) + confirm_not_paused() + test_action(save=True) + confirm_is_shutdown() + test_action(restore=True) + confirm_is_running() + + +def testManagerSaveCancelError(app): + """ + Test managed save special behavior + """ + app.open(extra_opts=["--test-options=test-managed-save"]) + + manager = app.topwin + run = manager.find("Run", "push button") + smenu = manager.find("Menu", "toggle button") + save = manager.find("Save", "menu item") + + c = manager.find("test-many-devices", "table cell") + c.click() + + # Save it, attempt a cancel operation + smenu.click() + save.click() + progwin = app.find_window("Saving Virtual Machine") + # Attempt cancel which will fail, then find the error message + progwin.find("Cancel", "push button").click() + progwin.find("Error cancelling save job") + lib.utils.check(lambda: not progwin.showing, timeout=5) + lib.utils.check(lambda: run.sensitive) + + # Restore will fail and offer to remove managed save + run.click() + app.click_alert_button("remove the saved state", "No") + lib.utils.check(lambda: run.sensitive) + run.click() + app.click_alert_button("remove the saved state", "Yes") + lib.utils.check(lambda: not run.sensitive) + + +def testManagerQEMUSetTime(app): + """ + Fake qemu setTime behavior for code coverage + """ + app.uri = tests.utils.URIs.kvm + manager = app.topwin + run = manager.find("Run", "push button") + smenu = manager.find("Menu", "toggle button") + save = manager.find("Save", "menu item") + + c = manager.find("test alternate devs title", "table cell") + c.click() + + # Save -> resume -> save + smenu.click() + save.click() + lib.utils.check(lambda: run.sensitive) + app.sleep(1) + run.click() + lib.utils.check(lambda: not run.sensitive) + app.sleep(1) + smenu.click() + save.click() + lib.utils.check(lambda: run.sensitive) + app.sleep(1) + + +def testManagerVMRunFail(app): + # Force VM startup to fail so we can test the error path + app.open(extra_opts=["--test-options=test-vm-run-fail"]) + + manager = app.topwin + + c = manager.find("test-clone-simple", "table cell") + c.click() + manager.find("Run", "push button").click() + app.click_alert_button("fake error", "Close") + + + +def testManagerColumns(app): + # Enable all stat options + # Need to expand the window size so all columns are onscreen + app.open(keyfile="winsize.ini") + app.root.find("Edit", "menu").click() + app.root.find("Preferences", "menu item").click() + win = app.find_window("Preferences") + win.find("Polling", "page tab").click() + win.find_fuzzy("Poll Disk", "check").click() + win.find_fuzzy("Poll Network", "check").click() + win.find_fuzzy("Poll Memory", "check").click() + win.find("Close", "push button").click() + + manager = app.topwin + def _test_sort(name): + col = manager.find(name, "table column header") + col.check_onscreen() + # Trigger sorting + col.click() + col.click() + + def _click_column_menu(name): + manager.find("View", "menu").click() + menu = manager.find("Graph", "menu") + menu.point() + menu.find_fuzzy(name, "check menu item").click() + + def _test_column(name): + _click_column_menu(name) + _test_sort(name) + + _test_sort("Name") + _click_column_menu("Guest CPU") + _click_column_menu("Guest CPU") + _test_sort("CPU usage") + _test_column("Host CPU") + _test_column("Memory") + _test_column("Disk I/O") + _test_column("Network I/O") + + +def testManagerWindowReposition(app): + """ + Restore previous position when window is reopened + """ + manager = app.topwin + host = app.manager_open_host("Storage") + fmenu = host.find("File", "menu") + fmenu.click() + fmenu.find("View Manager", "menu item").click() + lib.utils.check(lambda: manager.active) + + manager.window_maximize() + newx = manager.position[0] + newy = manager.position[1] + manager.keyCombo("F4") + host.click_title() + host.find("File", "menu").click() + host.find("View Manager", "menu item").click() + lib.utils.check(lambda: manager.showing) + assert manager.position == (newx, newy) + + + +def testManagerWindowCleanup(app): + """ + Open migrate, clone, delete, newvm, details, host windows, close the + connection, make sure they all disappear + """ + def _drag(win): """ - Fake qemu setTime behavior for code coverage + Drag a window so it's not obscuring the manager window """ - self.app.uri = tests.utils.URIs.kvm - manager = self.app.topwin - run = manager.find("Run", "push button") - smenu = manager.find("Menu", "toggle button") - save = manager.find("Save", "menu item") + win.drag(1000, 1000) - c = manager.find("test alternate devs title", "table cell") - c.click() + manager = app.topwin + app.sleep(1) + manager.window_maximize() - # Save -> resume -> save - smenu.click() - save.click() - lib.utils.check(lambda: run.sensitive) - self.app.sleep(1) - run.click() - lib.utils.check(lambda: not run.sensitive) - self.app.sleep(1) - smenu.click() - save.click() - lib.utils.check(lambda: run.sensitive) - self.app.sleep(1) + # Open delete window hitting a special code path, then close it + manager.find("test-many-devices", "table cell").click() + manager.find("Edit", "menu").click() + manager.find("Delete", "menu item").click() + delete = app.root.find_fuzzy("Delete", "frame") + app.sleep(.5) + delete.click_title() + delete.window_close() - def testManagerVMRunFail(self): - # Force VM startup to fail so we can test the error path - self.app.open(extra_opts=["--test-options=test-vm-run-fail"]) + # Open Clone window hitting a special code path, then close it + manager.find("test-clone", "table cell").click() + app.rawinput.pressKey("Menu") + app.root.find("Clone...", "menu item").click() + clone = app.find_window("Clone Virtual Machine") + app.sleep(.5) + clone.click_title() + clone.window_close() - manager = self.app.topwin + # Open host + c = manager.find_fuzzy("testdriver.xml", "table cell") + c.click() + app.sleep(.5) + c.doubleClick() + host = app.find_window("test testdriver.xml - Connection Details") + _drag(host) - c = manager.find("test-clone-simple", "table cell") - c.click() - manager.find("Run", "push button").click() - self.app.click_alert_button("fake error", "Close") + # Open details + c = manager.find("test-many-devices", "table cell") + c.click() + app.sleep(.5) + c.doubleClick() + details = app.find_details_window("test-many-devices") + _drag(details) + + # Close the connection + app.sleep(.5) + manager.click_title() + app.sleep(.5) + app.manager_conn_disconnect("test testdriver.xml") + + # Ensure all those windows aren't showing + lib.utils.check(lambda: not details.showing) + + # Delete the connection, ensure the host dialog disappears + app.manager_conn_delete("test testdriver.xml") + lib.utils.check(lambda: not host.showing) - def testManagerColumns(self): - # Enable all stat options - # Need to expand the window size so all columns are onscreen - self.app.open(keyfile="winsize.ini") - self.app.root.find("Edit", "menu").click() - self.app.root.find("Preferences", "menu item").click() - win = self.app.root.find_fuzzy("Preferences", "frame") - win.find("Polling", "page tab").click() - win.find_fuzzy("Poll Disk", "check").click() - win.find_fuzzy("Poll Network", "check").click() - win.find_fuzzy("Poll Memory", "check").click() - win.find("Close", "push button").click() +def testManagerDefaultStartup(app): + app.open(use_uri=False) + manager = app.topwin + errlabel = manager.find("error-label") + lib.utils.check( + lambda: "Checking for virtualization" in errlabel.text) + lib.utils.check( + lambda: "File->Add Connection" in errlabel.text) + lib.utils.check( + lambda: "appropriate QEMU/KVM" in errlabel.text) - manager = self.app.topwin - def _test_sort(name): - col = manager.find(name, "table column header") - col.check_onscreen() - # Trigger sorting - col.click() - col.click() - - def _click_column_menu(name): - manager.find("View", "menu").click() - menu = manager.find("Graph", "menu") - menu.point() - menu.find_fuzzy(name, "check menu item").click() - - def _test_column(name): - _click_column_menu(name) - _test_sort(name) - - _test_sort("Name") - _click_column_menu("Guest CPU") - _click_column_menu("Guest CPU") - _test_sort("CPU usage") - _test_column("Host CPU") - _test_column("Memory") - _test_column("Disk I/O") - _test_column("Network I/O") - - def testManagerWindowReposition(self): - """ - Restore previous position when window is reopened - """ - manager = self.app.topwin - host = self.app.open_host_window("Storage") - fmenu = host.find("File", "menu") - fmenu.click() - fmenu.find("View Manager", "menu item").click() - lib.utils.check(lambda: manager.active) - - # Double click title to maximize - manager.click_title() - manager.click_title() - newx = manager.position[0] - newy = manager.position[1] - manager.keyCombo("F4") - host.click_title() - host.find("File", "menu").click() - host.find("View Manager", "menu item").click() - lib.utils.check(lambda: manager.showing) - assert manager.position == (newx, newy) + manager.find("File", "menu").click() + manager.find("Quit", "menu item").click() - def testManagerWindowCleanup(self): - """ - Open migrate, clone, delete, newvm, details, host windows, close the - connection, make sure they all disappear - """ - def _drag(win): - """ - Drag a window so it's not obscuring the manager window - """ - win.drag(1000, 1000) - - manager = self.app.topwin - - # Open migrate dialog - c = manager.find("test-many-devices", "table cell") - c.click(button=3) - self.app.root.find("Migrate...", "menu item").click() - migrate = self.app.root.find("Migrate the virtual machine", "frame") - _drag(migrate) - - # Open clone dialog - c = manager.find("test-clone", "table cell") - c.click() - self.app.rawinput.pressKey("Menu") - self.app.root.find("Clone...", "menu item").click() - clone = self.app.root.find("Clone Virtual Machine", "frame") - _drag(clone) - - # Open delete dialog - c.click() - manager.find("Edit", "menu").click() - manager.find("Delete", "menu item").click() - delete = self.app.root.find_fuzzy("Delete", "frame") - _drag(delete) - - # Open NewVM - self.app.root.find("New", "push button").click() - create = self.app.root.find("New VM", "frame") - _drag(create) - - # Open host - c = manager.find_fuzzy("testdriver.xml", "table cell") - c.doubleClick() - host = self.app.root.find_fuzzy("Connection Details", "frame") - _drag(host) - - # Open details - details = self.app.open_details_window("test-many-devices") - _drag(details) - - # Close the connection - self.app.sleep(1) - manager.click() - c = manager.find_fuzzy("testdriver.xml", "table cell") - c.click() - c.click(button=3) - self.app.root.find("conn-disconnect", "menu item").click() - - # Ensure all those windows aren't showing - lib.utils.check(lambda: not migrate.showing) - lib.utils.check(lambda: not clone.showing) - lib.utils.check(lambda: not create.showing) - lib.utils.check(lambda: not details.showing) - lib.utils.check(lambda: not delete.showing) - - # Delete the connection, ensure the host dialog disappears - c = manager.find_fuzzy("testdriver.xml", "table cell") - c.click(button=3) - self.app.root.find("conn-delete", "menu item").click() - self.app.click_alert_button("will remove the connection", "Yes") - lib.utils.check(lambda: not host.showing) - - def testManagerDefaultStartup(self): - self.app.open(use_uri=False) - manager = self.app.topwin - errlabel = manager.find("error-label") - lib.utils.check( - lambda: "Checking for virtualization" in errlabel.text) - lib.utils.check( - lambda: "File->Add Connection" in errlabel.text) - lib.utils.check( - lambda: "appropriate QEMU/KVM" in errlabel.text) - - manager.find("File", "menu").click() - manager.find("Quit", "menu item").click() - - def testManagerConnOpenFail(self): - self.app.open(keyfile="baduri.ini") - manager = self.app.topwin - manager.find_fuzzy("bad uri", "table cell").doubleClick() - lib.utils.check(lambda: not manager.active) - self.app.click_alert_button("Unable to connect", "Close") - lib.utils.check(lambda: manager.active) +def testManagerConnOpenFail(app): + app.open(keyfile="baduri.ini") + manager = app.topwin + manager.find_fuzzy("bad uri", "table cell").doubleClick() + lib.utils.check(lambda: not manager.active) + app.click_alert_button("Unable to connect", "Close") + lib.utils.check(lambda: manager.active) diff --git a/tests/uitests/test_mediachange.py b/tests/uitests/test_mediachange.py index fc8237c2..9abfe906 100644 --- a/tests/uitests/test_mediachange.py +++ b/tests/uitests/test_mediachange.py @@ -4,100 +4,101 @@ from . import lib -class MediaChange(lib.testcase.UITestCase): +############################################# +# UI tests for details storage media change # +############################################# + +def testMediaChange(app): + vmname = "test-many-devices" + app.open(show_console=vmname) + win = app.find_details_window(vmname, + click_details=True, shutdown=True) + hw = win.find("hw-list") + tab = win.find("disk-tab") + combo = win.find("media-combo") + entry = win.find("media-entry") + appl = win.find("config-apply") + + # Floppy + physical + hw.find("Floppy 1", "table cell").click() + combo.click_combo_entry() + combo.find(r"Floppy_install_label \(/dev/fdb\)") + lib.utils.check(lambda: entry.text == "No media detected (/dev/fda)") + entry.click() + entry.click_secondary_icon() + lib.utils.check(lambda: not entry.text) + appl.click() + lib.utils.check(lambda: not appl.sensitive) + lib.utils.check(lambda: not entry.text) + + # Enter /dev/fdb, after apply it should change to pretty label + entry.set_text("/dev/fdb") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + lib.utils.check(lambda: + entry.text == "Floppy_install_label (/dev/fdb)") + + # Specify manual path + path = "/tmp/aaaaaaaaaaaaaaaaaaaaaaa.img" + entry.set_text(path) + appl.click() + lib.utils.check(lambda: not appl.sensitive) + lib.utils.check(lambda: entry.text == path) + + # Go to Floppy 2, make sure previous path is in recent list + hw.find("Floppy 2", "table cell").click() + combo.click_combo_entry() + combo.find(path) + entry.click() + # Use the storage browser to select new floppy storage + tab.find("Browse", "push button").click() + app.select_storagebrowser_volume("default-pool", "iso-vol") + appl.click() + + # Browse for image + hw.find("IDE CDROM 1", "table cell").click() + combo.click_combo_entry() + combo.find(r"Fedora12_media \(/dev/sr0\)") + entry.click() + tab.find("Browse", "push button").click() + app.select_storagebrowser_volume("default-pool", "backingl1.img") + # Check 'already in use' dialog + appl.click() + app.click_alert_button("already in use by", "No") + lib.utils.check(lambda: appl.sensitive) + appl.click() + app.click_alert_button("already in use by", "Yes") + lib.utils.check(lambda: not appl.sensitive) + lib.utils.check(lambda: "backing" in entry.text) + entry.set_text("") + appl.click() + lib.utils.check(lambda: not appl.sensitive) + lib.utils.check(lambda: not entry.text) + + + +def testMediaHotplug(app): """ - UI tests for details storage media change + Test in the case of a running VM """ + vmname = "test-many-devices" + app.open(show_console=vmname) + win = app.find_details_window(vmname, click_details=True) + hw = win.find("hw-list") + entry = win.find("media-entry") + appl = win.find("config-apply") - ############## - # Test cases # - ############## + # CDROM + physical + hw.find("IDE CDROM 1", "table cell").click() + lib.utils.check(lambda: not entry.text) + entry.set_text("/dev/sr0") + appl.click() + app.click_alert_button("changes will take effect", "OK") + lib.utils.check(lambda: not appl.sensitive) + lib.utils.check(lambda: not entry.text) - def testMediaChange(self): - win = self.app.open_details_window("test-many-devices", shutdown=True) - hw = win.find("hw-list") - tab = win.find("disk-tab") - combo = win.find("media-combo") - entry = win.find("media-entry") - appl = win.find("config-apply") - - # Floppy + physical - hw.find("Floppy 1", "table cell").click() - combo.click_combo_entry() - combo.find(r"Floppy_install_label \(/dev/fdb\)") - lib.utils.check(lambda: entry.text == "No media detected (/dev/fda)") - entry.click() - entry.click_secondary_icon() - lib.utils.check(lambda: not entry.text) - appl.click() - lib.utils.check(lambda: not appl.sensitive) - lib.utils.check(lambda: not entry.text) - - # Enter /dev/fdb, after apply it should change to pretty label - entry.set_text("/dev/fdb") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - lib.utils.check(lambda: - entry.text == "Floppy_install_label (/dev/fdb)") - - # Specify manual path - path = "/tmp/aaaaaaaaaaaaaaaaaaaaaaa.img" - entry.set_text(path) - appl.click() - lib.utils.check(lambda: not appl.sensitive) - lib.utils.check(lambda: entry.text == path) - - # Go to Floppy 2, make sure previous path is in recent list - hw.find("Floppy 2", "table cell").click() - combo.click_combo_entry() - combo.find(path) - entry.click() - # Use the storage browser to select new floppy storage - tab.find("Browse", "push button").click() - self.app.select_storagebrowser_volume("default-pool", "iso-vol") - appl.click() - - # Browse for image - hw.find("IDE CDROM 1", "table cell").click() - combo.click_combo_entry() - combo.find(r"Fedora12_media \(/dev/sr0\)") - entry.click() - tab.find("Browse", "push button").click() - self.app.select_storagebrowser_volume("default-pool", "backingl1.img") - # Check 'already in use' dialog - appl.click() - self.app.click_alert_button("already in use by", "No") - lib.utils.check(lambda: appl.sensitive) - appl.click() - self.app.click_alert_button("already in use by", "Yes") - lib.utils.check(lambda: not appl.sensitive) - lib.utils.check(lambda: "backing" in entry.text) - entry.set_text("") - appl.click() - lib.utils.check(lambda: not appl.sensitive) - lib.utils.check(lambda: not entry.text) - - - def testMediaHotplug(self): - """ - Test in the case of a running VM - """ - win = self.app.open_details_window("test-many-devices") - hw = win.find("hw-list") - entry = win.find("media-entry") - appl = win.find("config-apply") - - # CDROM + physical - hw.find("IDE CDROM 1", "table cell").click() - lib.utils.check(lambda: not entry.text) - entry.set_text("/dev/sr0") - appl.click() - self.app.click_alert_button("changes will take effect", "OK") - lib.utils.check(lambda: not appl.sensitive) - lib.utils.check(lambda: not entry.text) - - # Shutdown the VM, verify change shows up - win.find("Shut Down", "push button").click() - run = win.find("Run", "push button") - lib.utils.check(lambda: run.sensitive) - lib.utils.check(lambda: entry.text == "Fedora12_media (/dev/sr0)") + # Shutdown the VM, verify change shows up + win.find("Shut Down", "push button").click() + run = win.find("Run", "push button") + lib.utils.check(lambda: run.sensitive) + lib.utils.check(lambda: entry.text == "Fedora12_media (/dev/sr0)") diff --git a/tests/uitests/test_migrate.py b/tests/uitests/test_migrate.py index 3d67b17f..0f2bcfe5 100644 --- a/tests/uitests/test_migrate.py +++ b/tests/uitests/test_migrate.py @@ -5,149 +5,147 @@ import tests.utils from . import lib -class VMMMigrate(lib.testcase.UITestCase): +################################### +# UI tests for the migrate dialog # +################################### + +def _open_migrate(app, vmname): + app.manager_vm_action(vmname, migrate=True) + return app.find_window("Migrate the virtual machine") + + +def testMigrateQemu(app): + # Use fake qemu connections + app.uri = tests.utils.URIs.kvm + newuri = (tests.utils.URIs.test_default + + ",fakeuri=qemu+tcp://fakehost/system") + app.manager_createconn(newuri) + + # Run default migrate + mig = _open_migrate(app, "test-many-devices") + mig.find("Migrate", "push button").click() + app.click_alert_button( + "the.connection.driver:.virDomainMigrate", "Close") + mig.find("Cancel", "push button").click() + lib.utils.check(lambda: not mig.showing) + + # Run with deselected URI + mig = _open_migrate(app, "test-many-devices") + mig.find("address-check").click() + label = mig.find("Let libvirt decide") + label.check_onscreen() + mig.find("Migrate", "push button").click() + app.click_alert_button( + "the.connection.driver:.virDomainMigrate", "Close") + mig.find("Cancel", "push button").click() + lib.utils.check(lambda: not mig.showing) + + # Run with tunnelled and other options + mig = _open_migrate(app, "test-many-devices") + mig.combo_select("Mode:", "Tunnelled") + mig.find("Advanced", "toggle button").click_expander() + mig.find("Allow unsafe:", "check box").click() + mig.find("Temporary", "check box").click() + + mig.find("Migrate", "push button").click() + app.click_alert_button("p2p migration", "Close") + mig.find("Cancel", "push button").click() + lib.utils.check(lambda: not mig.showing) + + +def testMigrateXen(app): + # Use fake xen connections + app.uri = tests.utils.URIs.test_full + ",fakeuri=xen:///" + + fakeremotexen = (tests.utils.URIs.test_default + + ",fakeuri=xen+tcp://fakehost/") + app.manager_createconn(fakeremotexen) + + # Run default migrate + mig = _open_migrate(app, "test-many-devices") + mig.find("Migrate", "push button").click() + app.click_alert_button( + "the.connection.driver:.virDomainMigrate", "Close") + mig.find("Cancel", "push button").click() + lib.utils.check(lambda: not mig.showing) + + +def testMigrateMock(app): """ - UI tests for the migrate dialog + Trigger the mock migration testing we have to emulate success """ + # Add an additional connection + app.manager_createconn("test:///default") - ############## - # Test cases # - ############## + # Run it and check some values + mig = _open_migrate(app, "test-many-devices") + mig.find("address-text").set_text("TESTSUITE-FAKE") - def _add_conn(self, uri): - manager = self.app.root - manager.find("File", "menu").click() - manager.find("Add Connection...", "menu item").click() - win = manager.find_fuzzy("Add Connection", "dialog") - win.combo_select("Hypervisor", "Custom URI") - win.find("uri-entry", "text").set_text(uri) - win.find("Connect", "push button").click() - lib.utils.check(lambda: win.showing is False) + mig.find("Migrate", "push button").click() + progwin = app.find_window("Migrating VM") + # Attempt cancel which will fail, then find the error message + progwin.find("Cancel", "push button").click() + progwin.find("Error cancelling migrate job") + lib.utils.check(lambda: not progwin.showing, timeout=5) + lib.utils.check(lambda: not mig.showing) - def _open_migrate(self, vmname): - c = self.app.root.find(vmname, "table cell") - c.click(button=3) - self.app.root.find("Migrate...", "menu item").click() - return self.app.root.find("Migrate the virtual machine", "frame") - def testMigrateQemu(self): - # Use fake qemu connections - self.app.uri = tests.utils.URIs.kvm - self._add_conn(tests.utils.URIs.test_default + - ",fakeuri=qemu+tcp://fakehost/system") +def testMigrateConnMismatch(app): + # Add a possible target but disconnect it + app.uri = tests.utils.URIs.test_default + manager = app.topwin + manager.window_maximize() + manager.click() + app.manager_conn_disconnect("test default") - # Run default migrate - mig = self._open_migrate("test-many-devices") - mig.find("Migrate", "push button").click() - self.app.click_alert_button( - "the.connection.driver:.virDomainMigrate", "Close") - mig.find("Cancel", "push button").click() - lib.utils.check(lambda: not mig.showing) + # Add a mismatched hv connection + fakexen = tests.utils.URIs.test_empty + ",fakeuri=xen:///" + app.manager_createconn(fakexen) - # Run with deselected URI - mig = self._open_migrate("test-many-devices") - mig.find("address-check").click() - label = mig.find("Let libvirt decide") - label.check_onscreen() - mig.find("Migrate", "push button").click() - self.app.click_alert_button( - "the.connection.driver:.virDomainMigrate", "Close") - mig.find("Cancel", "push button").click() - lib.utils.check(lambda: not mig.showing) + # Open dialog and confirm no conns are available + app.manager_createconn(tests.utils.URIs.test_full) + mig = _open_migrate(app, "test-many-devices") + mig.find("conn-combo").find("No usable", "menu item") - # Run with tunnelled and other options - mig = self._open_migrate("test-many-devices") - mig.combo_select("Mode:", "Tunnelled") - mig.find("Advanced", "toggle button").click_expander() - mig.find("Allow unsafe:", "check box").click() - mig.find("Temporary", "check box").click() + # Test explicit dialog 'delete' + mig.keyCombo("F4") + lib.utils.check(lambda: not mig.showing) - mig.find("Migrate", "push button").click() - self.app.click_alert_button("p2p migration", "Close") - mig.find("Cancel", "push button").click() - lib.utils.check(lambda: not mig.showing) + # Ensure disconnecting will close the dialog + manager.click_title() + mig = _open_migrate(app, "test-many-devices") + app.manager_conn_disconnect("test testdriver.xml") + lib.utils.check(lambda: not mig.showing) - def testMigrateXen(self): - # Use fake xen connections - self.app.uri = tests.utils.URIs.test_full + ",fakeuri=xen:///" - fakeremotexen = (tests.utils.URIs.test_default + - ",fakeuri=xen+tcp://fakehost/") - self._add_conn(fakeremotexen) +def testMigrateXMLEditor(app): + app.open(xmleditor_enabled=True) + manager = app.topwin - # Run default migrate - mig = self._open_migrate("test-many-devices") - mig.find("Migrate", "push button").click() - self.app.click_alert_button( - "the.connection.driver:.virDomainMigrate", "Close") - mig.find("Cancel", "push button").click() - lib.utils.check(lambda: not mig.showing) + # Add an additional connection + app.manager_createconn("test:///default") - def testMigrateMock(self): - """ - Trigger the mock migration testing we have to emulate success - """ - # Add an additional connection - self._add_conn("test:///default") + # Run it and check some values + vmname = "test-many-devices" + win = _open_migrate(app, vmname) + win.find("address-text").set_text("TESTSUITE-FAKE") - # Run it and check some values - mig = self._open_migrate("test-many-devices") - mig.find("address-text").set_text("TESTSUITE-FAKE") + # Create a new obj with XML edited name, verify it worked + newname = "aafroofroo" + win.find("XML", "page tab").click() + xmleditor = win.find("XML editor") + newtext = xmleditor.text.replace( + ">%s<" % vmname, ">%s<" % newname) + xmleditor.set_text(newtext) + win.find("Migrate", "push button").click() + lib.utils.check(lambda: not win.showing, timeout=10) - mig.find("Migrate", "push button").click() - progwin = self.app.root.find("Migrating VM", "frame") - # Attempt cancel which will fail, then find the error message - progwin.find("Cancel", "push button").click() - progwin.find("Error cancelling migrate job") - lib.utils.check(lambda: not progwin.showing, timeout=5) - lib.utils.check(lambda: not mig.showing) + manager.find(newname, "table cell") - def testMigrateConnMismatch(self): - # Add a possible target but disconnect it - self.app.uri = tests.utils.URIs.test_default - c = self.app.root.find("test default", "table cell") - c.click(button=3) - self.app.root.find("conn-disconnect", "menu item").click() - - # Add a mismatched hv connection - fakexen = tests.utils.URIs.test_empty + ",fakeuri=xen:///" - self._add_conn(fakexen) - - # Open dialog and confirm no conns are available - self._add_conn(tests.utils.URIs.test_full) - mig = self._open_migrate("test-many-devices") - mig.find("conn-combo").find("No usable", "menu item") - mig.keyCombo("F4") - lib.utils.check(lambda: not mig.showing) - - def testMigrateXMLEditor(self): - self.app.open(xmleditor_enabled=True) - manager = self.app.topwin - - # Add an additional connection - self._add_conn("test:///default") - - # Run it and check some values - vmname = "test-many-devices" - win = self._open_migrate(vmname) - win.find("address-text").set_text("TESTSUITE-FAKE") - - # Create a new obj with XML edited name, verify it worked - newname = "aafroofroo" - win.find("XML", "page tab").click() - xmleditor = win.find("XML editor") - newtext = xmleditor.text.replace( - ">%s<" % vmname, ">%s<" % newname) - xmleditor.set_text(newtext) - win.find("Migrate", "push button").click() - lib.utils.check(lambda: not win.showing, timeout=10) - - manager.find(newname, "table cell") - - # Do standard xmleditor tests - win = self._open_migrate(vmname) - win.find("address-text").set_text("TESTSUITE-FAKE") - finish = win.find("Migrate", "push button") - lib.utils.test_xmleditor_interactions(self.app, win, finish) - win.find("Cancel", "push button").click() - lib.utils.check(lambda: not win.visible) + # Do standard xmleditor tests + win = _open_migrate(app, vmname) + win.find("address-text").set_text("TESTSUITE-FAKE") + finish = win.find("Migrate", "push button") + lib.utils.test_xmleditor_interactions(app, win, finish) + win.find("Cancel", "push button").click() + lib.utils.check(lambda: not win.visible) diff --git a/tests/uitests/test_prefs.py b/tests/uitests/test_prefs.py index bd82406e..8a210482 100644 --- a/tests/uitests/test_prefs.py +++ b/tests/uitests/test_prefs.py @@ -4,137 +4,134 @@ from . import lib -class VMMPrefs(lib.testcase.UITestCase): +####################################### +# UI tests for the preferences dialog # +####################################### + +def testPrefsAll(app): + app.root.find("Edit", "menu").click() + app.root.find("Preferences", "menu item").click() + + win = app.find_window("Preferences") + generaltab = win.find("general-tab") + pollingtab = win.find("polling-tab") + newvmtab = win.find("newvm-tab") + consoletab = win.find("console-tab") + feedbacktab = win.find("feedback-tab") + + feedbacktab.check_not_onscreen() + tab = generaltab + tab.check_onscreen() + tab.find_fuzzy("Enable system tray", "check").click() + tab.find_fuzzy("Enable XML").click() + tab.find_fuzzy("libguestfs VM").click() + + win.find("Polling", "page tab").click() + tab = pollingtab + tab.check_onscreen() + tab.find("Poll CPU", "check box").click() + tab.find("Poll Disk", "check box").click() + tab.find("Poll Memory", "check box").click() + tab.find("Poll Network", "check box").click() + period = tab.find_fuzzy("cpu-poll", "spin button") + period.click() + period.set_text("5") + + win.find("New VM", "page tab").click() + tab = newvmtab + tab.check_onscreen() + tab.find("Add sound device", "check box").click() + tab.combo_select("CPU default:", "Copy host") + tab.combo_select("Storage format:", "Raw") + tab.combo_select("prefs-add-spice-usbredir", "No") + tab.combo_select("Graphics type", "VNC") + + win.find("Console", "page tab").click() + tab = consoletab + tab.check_onscreen() + tab.combo_select("SPICE USB", "Manual redirect") + tab.combo_select("Resize guest", "On") + tab.combo_select("Graphical console scaling", "Always") + tab.find("Console autoconnect", "check box").click() + + tab.find("Change...", "push button").click() + keyframe = app.find_window("Configure grab") + + # On certain environments pressing "Alt_L" and + # clicking a window starts window drag operation. + # Work around by pushing both Control and Alt. + app.rawinput.holdKey("Control_L") + app.rawinput.holdKey("Alt_L") + app.rawinput.holdKey("Z") + + # Test releasekey handler + app.rawinput.releaseKey("Z") + app.rawinput.holdKey("Z") + try: + keyframe.find_fuzzy("OK", "push button").click() + finally: + app.rawinput.releaseKey("Z") + app.rawinput.releaseKey("Alt_L") + app.rawinput.releaseKey("Control_L") + + win.find("Feedback", "page tab").click() + tab = feedbacktab + tab.check_onscreen() + tab.find("Force Poweroff", "check box").click() + tab.find("Poweroff/Reboot", "check box").click() + tab.find("Pause", "check box").click() + tab.find("Device removal", "check box").click() + tab.find("Unapplied changes", "check box").click() + tab.find("Deleting storage", "check box").click() + + win.find("General", "page tab").click() + win.find_fuzzy("Enable system tray", "check").click() + + win.find_fuzzy("Close", "push button").click() + lib.utils.check(lambda: win.visible is False) + + + +def testPrefsXMLEditor(app): + managerwin = app.topwin + managerwin.drag(0, 200) + detailswin = app.manager_open_details("test-clone-simple") + finish = detailswin.find("config-apply") + xmleditor = detailswin.find("XML editor") + + detailswin.find("XML", "page tab").click() + warnlabel = detailswin.find_fuzzy("XML editing is disabled") + lib.utils.check(lambda: warnlabel.visible) + origtext = xmleditor.text + xmleditor.typeText("1234abcd") + lib.utils.check(lambda: xmleditor.text == origtext) + + managerwin.click_title() + managerwin.grabFocus() + managerwin.find("Edit", "menu").click() + managerwin.find("Preferences", "menu item").click() + prefswin = app.find_window("Preferences") + prefswin.find_fuzzy("Enable XML").click() + prefswin.find_fuzzy("Close", "push button").click() + lib.utils.check(lambda: prefswin.visible is False) + + managerwin.keyCombo("F4") + detailswin.click() + newtext = xmleditor.text.replace(">", ">FOOTITLE", 1) + xmleditor.set_text(newtext) + finish.click() + detailswin.find("Details", "page tab").click() + lib.utils.check(lambda: + detailswin.find("Title:", "text").text == "FOOTITLE") + + +def testPrefsKeyfile(app): """ - UI tests for the preferences dialog + Preload some keyfile settings and verify they work as expected """ + app.open(use_uri=False, keyfile="defaultconn.ini") + managerwin = app.topwin - ############## - # Test cases # - ############## - - def testPrefsAll(self): - self.app.root.find("Edit", "menu").click() - self.app.root.find("Preferences", "menu item").click() - - win = self.app.root.find_fuzzy("Preferences", "frame") - generaltab = win.find("general-tab") - pollingtab = win.find("polling-tab") - newvmtab = win.find("newvm-tab") - consoletab = win.find("console-tab") - feedbacktab = win.find("feedback-tab") - - feedbacktab.check_not_onscreen() - tab = generaltab - tab.check_onscreen() - tab.find_fuzzy("Enable system tray", "check").click() - tab.find_fuzzy("Enable XML").click() - tab.find_fuzzy("libguestfs VM").click() - - win.find("Polling", "page tab").click() - tab = pollingtab - tab.check_onscreen() - tab.find("Poll CPU", "check box").click() - tab.find("Poll Disk", "check box").click() - tab.find("Poll Memory", "check box").click() - tab.find("Poll Network", "check box").click() - period = tab.find_fuzzy("cpu-poll", "spin button") - period.click() - period.set_text("5") - - win.find("New VM", "page tab").click() - tab = newvmtab - tab.check_onscreen() - tab.find("Add sound device", "check box").click() - tab.combo_select("CPU default:", "Copy host") - tab.combo_select("Storage format:", "Raw") - tab.combo_select("prefs-add-spice-usbredir", "No") - tab.combo_select("Graphics type", "VNC") - - win.find("Console", "page tab").click() - tab = consoletab - tab.check_onscreen() - tab.combo_select("SPICE USB", "Manual redirect") - tab.combo_select("Resize guest", "On") - tab.combo_select("Graphical console scaling", "Always") - tab.find("Console autoconnect", "check box").click() - - tab.find("Change...", "push button").click() - keyframe = self.app.root.find_fuzzy("Configure grab", "dialog") - - # On certain environments pressing "Alt_L" and - # clicking a window starts window drag operation. - # Work around by pushing both Control and Alt. - self.app.rawinput.holdKey("Control_L") - self.app.rawinput.holdKey("Alt_L") - self.app.rawinput.holdKey("Z") - - # Test releasekey handler - self.app.rawinput.releaseKey("Z") - self.app.rawinput.holdKey("Z") - try: - keyframe.find_fuzzy("OK", "push button").click() - finally: - self.app.rawinput.releaseKey("Z") - self.app.rawinput.releaseKey("Alt_L") - self.app.rawinput.releaseKey("Control_L") - - win.find("Feedback", "page tab").click() - tab = feedbacktab - tab.check_onscreen() - tab.find("Force Poweroff", "check box").click() - tab.find("Poweroff/Reboot", "check box").click() - tab.find("Pause", "check box").click() - tab.find("Device removal", "check box").click() - tab.find("Unapplied changes", "check box").click() - tab.find("Deleting storage", "check box").click() - - win.find("General", "page tab").click() - win.find_fuzzy("Enable system tray", "check").click() - - win.find_fuzzy("Close", "push button").click() - lib.utils.check(lambda: win.visible is False) - - - def testPrefsXMLEditor(self): - managerwin = self.app.topwin - managerwin.drag(0, 200) - detailswin = self.app.open_details_window("test-clone-simple") - finish = detailswin.find("config-apply") - xmleditor = detailswin.find("XML editor") - - detailswin.find("XML", "page tab").click() - warnlabel = detailswin.find_fuzzy("XML editing is disabled") - lib.utils.check(lambda: warnlabel.visible) - origtext = xmleditor.text - xmleditor.typeText("1234abcd") - lib.utils.check(lambda: xmleditor.text == origtext) - - managerwin.click_title() - managerwin.grabFocus() - managerwin.find("Edit", "menu").click() - managerwin.find("Preferences", "menu item").click() - prefswin = self.app.root.find_fuzzy("Preferences", "frame") - prefswin.find_fuzzy("Enable XML").click() - prefswin.find_fuzzy("Close", "push button").click() - lib.utils.check(lambda: prefswin.visible is False) - - managerwin.keyCombo("F4") - detailswin.click() - newtext = xmleditor.text.replace(">", ">FOOTITLE", 1) - xmleditor.set_text(newtext) - finish.click() - detailswin.find("Details", "page tab").click() - lib.utils.check(lambda: - detailswin.find("Title:", "text").text == "FOOTITLE") - - def testPrefsKeyfile(self): - """ - Preload some keyfile settings and verify they work as expected - """ - self.app.open(use_uri=False, keyfile="defaultconn.ini") - managerwin = self.app.topwin - - # test:///default should be connected - managerwin.find("test default", "table cell") - managerwin.find("foo - Not Connected", "table cell") + # test:///default should be connected + managerwin.find("test default", "table cell") + managerwin.find("foo - Not Connected", "table cell") diff --git a/tests/uitests/test_snapshot.py b/tests/uitests/test_snapshot.py index 93743e89..4eb478cf 100644 --- a/tests/uitests/test_snapshot.py +++ b/tests/uitests/test_snapshot.py @@ -4,189 +4,177 @@ from . import lib -class Snapshots(lib.testcase.UITestCase): +############################################ +# UI tests for virt-manager's VM snapshots # +############################################ + +_DEFAULT_VMNAME = "test-snapshots" + + +def _open_snapshots_window(app, vmname=_DEFAULT_VMNAME): + if not app.is_running(): + app.open(show_console=vmname) + win = app.find_details_window(vmname) + win.find("Snapshots", "radio button").click() + return win + + +def testSnapshotsSmokeTest(app): """ - UI tests for virt-manager's VM snapshots + Smoke test to ensure all snapshots show correctly """ - - ################### - # Private helpers # - ################### - - def _open_snapshots_window(self, vmname="test-snapshots"): - self.app.root.find_fuzzy(vmname, "table cell").click(button=3) - self.app.root.find("Open", "menu item").click() - - win = self.app.root.find("%s on" % vmname, "frame") - win.find("Snapshots", "radio button").click() - return win + win = _open_snapshots_window(app) + errlabel = win.find("snapshot-error-label", "label") + lst = win.find("snapshot-list", "table") + lib.utils.walkUIList(app, win, lst, lambda: errlabel.showing) - ############## - # Test cases # - ############## +def testSnapshotLifecycle(app): + """ + Create/delete/start/stop snapshots + """ + win = _open_snapshots_window(app) + vmrun = win.find("Run", "push button") + vmpause = win.find("Pause", "toggle button") + snaprun = win.find("snapshot-start", "push button") - def testSnapshotsSmokeTest(self): - """ - Smoke test to ensure all snapshots show correctly - """ - win = self._open_snapshots_window() - errlabel = win.find("snapshot-error-label", "label") - lst = win.find("snapshot-list", "table") - lib.utils.walkUIList(self.app, win, lst, lambda: errlabel.showing) + # Start already running snapshot + snapname = "internal-root" + win.find(snapname, "table cell").click() + snaprun.click() + app.click_alert_button("run the snapshot '%s'" % snapname, "Yes") + lib.utils.check(lambda: not vmrun.sensitive) - def testSnapshotLifecycle(self): - """ - Create/delete/start/stop snapshots - """ - win = self._open_snapshots_window() - vmrun = win.find("Run", "push button") - vmpause = win.find("Pause", "toggle button") - snaprun = win.find("snapshot-start", "push button") + # Start offline snapshot + snapname = "offline-root" + win.find(snapname, "table cell").click() + snaprun.click() + app.click_alert_button("run the snapshot '%s'" % snapname, "No") + lib.utils.check(lambda: not vmrun.sensitive) + snaprun.click() + app.click_alert_button("run the snapshot '%s'" % snapname, "Yes") + lib.utils.check(lambda: vmrun.sensitive) - # Start already running snapshot - snapname = "internal-root" - win.find(snapname, "table cell").click() - snaprun.click() - self.app.click_alert_button("run the snapshot '%s'" % snapname, "Yes") - lib.utils.check(lambda: not vmrun.sensitive) + # Start paused snapshot + snapname = "snap-paused" + win.find(snapname, "table cell").click() + snaprun.click() + app.click_alert_button("run the snapshot '%s'" % snapname, "Yes") + lib.utils.check(lambda: vmpause.checked) - # Start offline snapshot - snapname = "offline-root" - win.find(snapname, "table cell").click() - snaprun.click() - self.app.click_alert_button("run the snapshot '%s'" % snapname, "No") - lib.utils.check(lambda: not vmrun.sensitive) - snaprun.click() - self.app.click_alert_button("run the snapshot '%s'" % snapname, "Yes") - lib.utils.check(lambda: vmrun.sensitive) + # Edit snapshot + descui = win.find("snapshot-description") + desc = "TESTSNAP" + descui.set_text(desc) + win.find("snapshot-apply", "push button").click() + win.find("snapshot-refresh", "push button").click() + lib.utils.check(lambda: descui.text == desc) + # Apply by clicking away + desc += " ROUND2" + descui.set_text(desc) + win.find("internal-root", "table cell").click() + app.click_alert_button("There are unapplied changes", "Yes") - # Start paused snapshot - snapname = "snap-paused" - win.find(snapname, "table cell").click() - snaprun.click() - self.app.click_alert_button("run the snapshot '%s'" % snapname, "Yes") - lib.utils.check(lambda: vmpause.checked) + # Create new snapshot + win.find("snapshot-add", "push button").click() + newwin = app.find_window("Create snapshot") + snapname = "testnewsnap" + newwin.find("Name:", "text").set_text(snapname) + newwin.find("Description:", "text").set_text("testdesc") + newwin.find("Finish", "push button").click() + lib.utils.check(lambda: not newwin.showing) + newc = win.find(snapname, "table cell") + lib.utils.check(lambda: newc.state_selected) - # Edit snapshot - descui = win.find("snapshot-description") - desc = "TESTSNAP" - descui.set_text(desc) - win.find("snapshot-apply", "push button").click() - win.find("snapshot-refresh", "push button").click() - lib.utils.check(lambda: descui.text == desc) - # Apply by clicking away - desc += " ROUND2" - descui.set_text(desc) - win.find("internal-root", "table cell").click() - self.app.click_alert_button("There are unapplied changes", "Yes") + # Delete it + win.find("snapshot-delete", "push button").click() + app.click_alert_button("permanently delete", "No") + lib.utils.check(lambda: not newc.dead) + win.find("snapshot-delete", "push button").click() + app.click_alert_button("permanently delete", "Yes") + lib.utils.check(lambda: newc.dead) - # Create new snapshot - win.find("snapshot-add", "push button").click() - newwin = self.app.root.find("Create snapshot", "frame") - snapname = "testnewsnap" - newwin.find("Name:", "text").set_text(snapname) - newwin.find("Description:", "text").set_text("testdesc") - newwin.find("Finish", "push button").click() - lib.utils.check(lambda: not newwin.showing) - newc = win.find(snapname, "table cell") - lib.utils.check(lambda: newc.state_selected) + # Recreate another snapshot with the same name + win.find("snapshot-add", "push button").click() + newwin = app.find_window("Create snapshot") + snapname = "testnewsnap" + newwin.find("Name:", "text").set_text(snapname) + newwin.find("Finish", "push button").click() + lib.utils.check(lambda: not newwin.showing) + newc = win.find(snapname, "table cell") + lib.utils.check(lambda: newc.state_selected) - # Delete it - win.find("snapshot-delete", "push button").click() - self.app.click_alert_button("permanently delete", "No") - lib.utils.check(lambda: not newc.dead) - win.find("snapshot-delete", "push button").click() - self.app.click_alert_button("permanently delete", "Yes") - lib.utils.check(lambda: newc.dead) + # Switch out of window + win.find("Details", "radio button").click() + lib.utils.check(lambda: not snaprun.showing) - # Recreate another snapshot with the same name - win.find("snapshot-add", "push button").click() - newwin = self.app.root.find("Create snapshot", "frame") - snapname = "testnewsnap" - newwin.find("Name:", "text").set_text(snapname) - newwin.find("Finish", "push button").click() - lib.utils.check(lambda: not newwin.showing) - newc = win.find(snapname, "table cell") - lib.utils.check(lambda: newc.state_selected) - # Switch out of window - win.find("Details", "radio button").click() - lib.utils.check(lambda: not snaprun.showing) +def testSnapshotMisc1(app): + """ + Test snapshot corner cases + """ + vmname = "test-state-managedsave" + win = _open_snapshots_window(app, vmname) + vmrun = win.find("Restore", "push button") - def testSnapshotMisc1(self): - """ - Test snapshot corner cases - """ - manager = self.app.topwin - manager.find("vm-list").click() - for ignore in range(8): - self.app.rawinput.pressKey("Down") - vmname = "test-state-managedsave" - cell = manager.find_fuzzy(vmname, "table cell") - cell.bring_on_screen() + # Create new snapshot + win.find("snapshot-add", "push button").click() + app.click_alert_button("not become part of the snapshot", "Cancel") + lib.utils.check(lambda: win.active) + win.find("snapshot-add", "push button").click() + app.click_alert_button("not become part of the snapshot", "OK") + newwin = app.find_window("Create snapshot") + snapname1 = "testnewsnap1" + newwin.find("Name:", "text").set_text(snapname1) + newwin.find("Finish", "push button").click() + lib.utils.check(lambda: not newwin.showing) + newc = win.find(snapname1, "table cell") + lib.utils.check(lambda: newc.state_selected) - win = self._open_snapshots_window(vmname=vmname) - vmrun = win.find("Restore", "push button") + # Start the VM, create another snapshot + vmrun.click() + lib.utils.check(lambda: not vmrun.sensitive) + win.find("snapshot-add", "push button").click() + newwin = app.find_window("Create snapshot") + # Force validation error + newwin.find("Name:", "text").set_text("bad name") + newwin.find("Finish", "push button").click() + app.click_alert_button("validating snapshot", "OK") + # Force name collision + newwin.find("Name:", "text").set_text(snapname1) + newwin.find("Finish", "push button").click() + app.click_alert_button(snapname1, "Close") + # Make it succeed + snapname2 = "testnewsnap2" + newwin.find("Name:", "text").set_text(snapname2) + newwin.find("Finish", "push button").click() + lib.utils.check(lambda: not newwin.showing) + newc = win.find(snapname2, "table cell") + lib.utils.check(lambda: newc.state_selected) - # Create new snapshot - win.find("snapshot-add", "push button").click() - self.app.click_alert_button("not become part of the snapshot", "Cancel") - lib.utils.check(lambda: win.active) - win.find("snapshot-add", "push button").click() - self.app.click_alert_button("not become part of the snapshot", "OK") - newwin = self.app.root.find("Create snapshot", "frame") - snapname1 = "testnewsnap1" - newwin.find("Name:", "text").set_text(snapname1) - newwin.find("Finish", "push button").click() - lib.utils.check(lambda: not newwin.showing) - newc = win.find(snapname1, "table cell") - lib.utils.check(lambda: newc.state_selected) + # Trigger another managed save warning + smenu = win.find("Menu", "toggle button") + smenu.click() + save = smenu.find("Save", "menu item") + save.click() + lib.utils.check(lambda: vmrun.sensitive) + win.find(snapname1, "table cell").click(button=3) + app.root.find("Start snapshot", "menu item").click() + app.click_alert_button("run the snapshot '%s'" % snapname1, "Yes") + app.click_alert_button("no memory state", "Cancel") + win.find("snapshot-start").click() + app.click_alert_button("run the snapshot '%s'" % snapname1, "Yes") + app.click_alert_button("no memory state", "OK") - # Start the VM, create another snapshot - vmrun.click() - lib.utils.check(lambda: not vmrun.sensitive) - win.find("snapshot-add", "push button").click() - newwin = self.app.root.find("Create snapshot", "frame") - # Force validation error - newwin.find("Name:", "text").set_text("bad name") - newwin.find("Finish", "push button").click() - self.app.click_alert_button("validating snapshot", "OK") - # Force name collision - newwin.find("Name:", "text").set_text(snapname1) - newwin.find("Finish", "push button").click() - self.app.click_alert_button(snapname1, "Close") - # Make it succeed - snapname2 = "testnewsnap2" - newwin.find("Name:", "text").set_text(snapname2) - newwin.find("Finish", "push button").click() - lib.utils.check(lambda: not newwin.showing) - newc = win.find(snapname2, "table cell") - lib.utils.check(lambda: newc.state_selected) - - # Trigger another managed save warning - smenu = win.find("Menu", "toggle button") - smenu.click() - save = smenu.find("Save", "menu item") - save.click() - lib.utils.check(lambda: vmrun.sensitive) - win.find(snapname1, "table cell").click(button=3) - self.app.root.find("Start snapshot", "menu item").click() - self.app.click_alert_button("run the snapshot '%s'" % snapname1, "Yes") - self.app.click_alert_button("no memory state", "Cancel") - win.find("snapshot-start").click() - self.app.click_alert_button("run the snapshot '%s'" % snapname1, "Yes") - self.app.click_alert_button("no memory state", "OK") - - # Multi select - cell1 = win.find(snapname1, "table cell") - cell2 = win.find(snapname2, "table cell") - cell1.click() - self.app.rawinput.holdKey("Shift_L") - self.app.rawinput.pressKey("Down") - self.app.rawinput.releaseKey("Shift_L") - win.find("snapshot-delete").click() - self.app.click_alert_button("permanently delete", "Yes") - lib.utils.check(lambda: cell1.dead) - lib.utils.check(lambda: cell2.dead) - lib.utils.check(lambda: win.active) + # Multi select + cell1 = win.find(snapname1, "table cell") + cell2 = win.find(snapname2, "table cell") + cell1.click() + app.rawinput.holdKey("Shift_L") + app.rawinput.pressKey("Down") + app.rawinput.releaseKey("Shift_L") + win.find("snapshot-delete").click() + app.click_alert_button("permanently delete", "Yes") + lib.utils.check(lambda: cell1.dead) + lib.utils.check(lambda: cell2.dead) + lib.utils.check(lambda: win.active) diff --git a/tests/uitests/test_systray.py b/tests/uitests/test_systray.py index f3138066..67d48c25 100644 --- a/tests/uitests/test_systray.py +++ b/tests/uitests/test_systray.py @@ -5,120 +5,113 @@ import tests.utils from . import lib -class Systray(lib.testcase.UITestCase): - """ - UI tests for virt-manager's systray using a fake testing backend - """ +#################################################################### +# UI tests for virt-manager's systray using a fake testing backend # +#################################################################### - ############## - # Test cases # - ############## +def testSystrayFake(app): + app.open( + keyfile="systray.ini", + extra_opts=["--test-options=fake-systray"], + window_name="Virtual Machine Manager") - def testSystrayFake(self): - self.app.open( - keyfile="systray.ini", - extra_opts=["--test-options=fake-systray"], - window_name="Virtual Machine Manager") + manager = app.topwin + systray = app.root.find("vmm-fake-systray", check_active=False) + manager.drag(1000, 1000) - manager = self.app.topwin - systray = self.app.root.find("vmm-fake-systray", check_active=False) - manager.drag(1000, 1000) + # Add a connection to trigger systray update + uri = tests.utils.URIs.kvm + app.manager_createconn(uri=uri) - # Add a connection to trigger systray update - uri = tests.utils.URIs.kvm - manager.find("File", "menu").click() - manager.find("Add Connection...", "menu item").click() - win = self.app.root.find_fuzzy("Add Connection", "dialog") - win.combo_select("Hypervisor", "Custom URI") - win.find("uri-entry", "text").set_text(uri) - win.find("Connect", "push button").click() + # Hide the manager + systray.click_title() + systray.click() + lib.utils.check(lambda: not manager.showing) + lib.utils.check(lambda: app.is_running()) - # Hide the manager - systray.click_title() - systray.click() - lib.utils.check(lambda: not manager.showing) - lib.utils.check(lambda: self.app.is_running()) + systray.click(button=3) + menu = app.root.find("vmm-systray-menu") - systray.click(button=3) - menu = self.app.root.find("vmm-systray-menu") - - def _get_conn_action(connstr, actionstr): - if not menu.showing: - systray.click(button=3) - lib.utils.check(lambda: menu.showing) - connmenu = menu.find(connstr, "menu") - connmenu.point() - return connmenu.find(actionstr, "menu") - - def _check_conn_action(connstr, actionstr): - item = _get_conn_action(connstr, actionstr) - lib.utils.check(lambda: item.showing) + def _get_conn_action(connstr, actionstr): + if not menu.showing: systray.click(button=3) - lib.utils.check(lambda: not menu.showing) + lib.utils.check(lambda: menu.showing) + connmenu = menu.find(connstr, "menu") + connmenu.point() + ret = connmenu.find(actionstr, "menu") + ret.check_onscreen() + return ret - def _do_conn_action(connstr, actionstr): - item = _get_conn_action(connstr, actionstr) - item.click() - lib.utils.check(lambda: not menu.showing) + def _check_conn_action(connstr, actionstr): + item = _get_conn_action(connstr, actionstr) + lib.utils.check(lambda: item.showing) + app.rawinput.pressKey("Escape") + lib.utils.check(lambda: not menu.showing) - def _get_vm_action(connstr, vmname, action): - vmenu = _get_conn_action(connstr, vmname) - vmenu.point() - return vmenu.find(action, "menu") + def _do_conn_action(connstr, actionstr): + item = _get_conn_action(connstr, actionstr) + item.click() + lib.utils.check(lambda: not menu.showing) - def _check_vm_action(connstr, vmname, action): - item = _get_vm_action(connstr, vmname, action) - lib.utils.check(lambda: item.showing) - systray.click(button=3) - lib.utils.check(lambda: not menu.showing) + def _get_vm_action(connstr, vmname, action): + vmenu = _get_conn_action(connstr, vmname) + vmenu.point() + return vmenu.find(action, "menu") - def _do_vm_action(connstr, vmname, action): - item = _get_vm_action(connstr, vmname, action) - item.click() - lib.utils.check(lambda: not menu.showing) + def _check_vm_action(connstr, vmname, action): + item = _get_vm_action(connstr, vmname, action) + lib.utils.check(lambda: item.showing) + app.rawinput.pressKey("Escape") + lib.utils.check(lambda: not menu.showing) - # Right click start a connection - _check_conn_action("QEMU/KVM", "Disconnect") - _do_conn_action("test default", "Connect") - _check_conn_action("test default", "Disconnect") - _do_conn_action("test testdriver", "Disconnect") - _check_conn_action("test testdriver", "Connect") + def _do_vm_action(connstr, vmname, action): + item = _get_vm_action(connstr, vmname, action) + item.click() + lib.utils.check(lambda: not menu.showing) - # Trigger VM change - _do_vm_action("QEMU/KVM", "test-arm-kernel", "Pause") - _check_vm_action("QEMU/KVM", "test-arm-kernel", "Resume") + # Right click start a connection + _check_conn_action("QEMU/KVM", "Disconnect") + _do_conn_action("test default", "Connect") + _check_conn_action("test default", "Disconnect") + _do_conn_action("test testdriver", "Disconnect") + _check_conn_action("test testdriver", "Connect") - # Reshow the manager - systray.click() - lib.utils.check(lambda: manager.showing) - lib.utils.check(lambda: self.app.is_running()) + # Trigger VM change + _do_vm_action("QEMU/KVM", "test-arm-kernel", "Pause") + _check_vm_action("QEMU/KVM", "test-arm-kernel", "Resume") - # Close from the menu - systray.click_title() - systray.click(button=3) - menu = self.app.root.find("vmm-systray-menu") - menu.find("Quit", "menu item").click() + # Reshow the manager + systray.click() + lib.utils.check(lambda: manager.showing) + lib.utils.check(lambda: app.is_running()) - lib.utils.check(lambda: not self.app.is_running()) + # Close from the menu + systray.click_title() + systray.click(button=3) + menu = app.root.find("vmm-systray-menu") + menu.find("Quit", "menu item").click() - def testSystrayToggle(self): - self.app.open( - keyfile="systray.ini", - extra_opts=["--test-options=fake-systray"], - window_name="Virtual Machine Manager") + lib.utils.check(lambda: not app.is_running()) - manager = self.app.topwin - systray = self.app.root.find("vmm-fake-systray", check_active=False) - manager.find("Edit", "menu").click() - manager.find("Preferences", "menu item").click() - prefs = self.app.root.find_fuzzy("Preferences", "frame") - # Close the system tray - prefs.click_title() - prefs.find_fuzzy("Enable system tray", "check").click() - lib.utils.check(lambda: not systray.showing) +def testSystrayToggle(app): + app.open( + keyfile="systray.ini", + extra_opts=["--test-options=fake-systray"], + window_name="Virtual Machine Manager") - # Close the manager - manager.click_title() - manager.keyCombo("F4") - lib.utils.check(lambda: not self.app.is_running()) + manager = app.topwin + systray = app.root.find("vmm-fake-systray", check_active=False) + manager.find("Edit", "menu").click() + manager.find("Preferences", "menu item").click() + prefs = app.find_window("Preferences") + + # Close the system tray + prefs.click_title() + prefs.find_fuzzy("Enable system tray", "check").click() + lib.utils.check(lambda: not systray.showing) + + # Close the manager + manager.click_title() + manager.keyCombo("F4") + lib.utils.check(lambda: not app.is_running()) diff --git a/virtManager/connection.py b/virtManager/connection.py index 23cd921e..35784fb6 100644 --- a/virtManager/connection.py +++ b/virtManager/connection.py @@ -1185,7 +1185,7 @@ class vmmConnection(vmmGObject): if self.is_disconnected(): return # pragma: no cover if self.is_connecting() and not force: - return + return # pragma: no cover # We need to set this before the event check, since stats polling # is independent of events diff --git a/virtManager/object/domain.py b/virtManager/object/domain.py index 25a3caee..c0a0f6ea 100644 --- a/virtManager/object/domain.py +++ b/virtManager/object/domain.py @@ -1377,7 +1377,7 @@ class vmmDomain(vmmLibvirtObject): start_job_progress_thread(self, meter, _("Saving domain to disk")) if self.config.CLITestOptions.test_managed_save: - time.sleep(3) + time.sleep(1.2) self._backend.managedSave(0) def has_managed_save(self): @@ -1437,7 +1437,7 @@ class vmmDomain(vmmLibvirtObject): if self.conn.is_test() and "TESTSUITE-FAKE" in (dest_uri or ""): # If using the test driver and a special URI, fake successful # migration so we can test more of the migration wizard - time.sleep(3) + time.sleep(1.2) if not xml: xml = self.get_xml_to_define() destconn.define_domain(xml).create()