# This work is licensed under the GNU GPLv2 or later. # See the COPYING file in the top-level directory. import os import shutil import tests.utils from . import lib class _DeleteRow: """ Helper class for interacting with the delete dialog rows """ def __init__(self, cell1, cell2, cell3, cell4): ignore = cell4 self.chkcell = cell1 self.path = cell2.text self.target = cell3.text self.undeletable = not self.chkcell.sensitive self.default = self.chkcell.checked self.notdefault = not self.undeletable and not self.default def _create_testdriver_path(fn): 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) try: if not os.path.exists(tmpdir): os.mkdir(tmpdir) open(tmppath, "w").write("foo") os.chmod(tmppath, 0o444) return fn(app, tmppath, *args, **kwargs) finally: if os.path.exists(tmpdir): os.chmod(tmpdir, 0o777) shutil.rmtree(tmpdir) return wrapper 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(app, vmname): app.manager_vm_action(vmname, delete=True) return app.find_window("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 = "/pool-dir/overlay.img" nondefault_path2 = "/pool-dir/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") slist.click() chkcell = found[0].chkcell chkcell.bring_on_screen() chkcell.click() chkcell.click() chkcell.click() lib.utils.check(lambda: chkcell.checked) 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("pool-dir", "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") @_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) @_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) @_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") # Ensure disconnecting will close the dialog win = _open_delete(app, "test-clone") app.manager_test_conn_window_cleanup("test testdriver.xml", win) def testDeleteRemoteManyDevices(app): """ Test with a remote VM to hit a certain code path """ app.uri = tests.utils.URIs.kvm_x86_remote _testDeleteManyDevices(app) 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") lib.utils.check(lambda: chk.checked) chk.click() lib.utils.check(lambda: not chk.checked) lib.utils.check(lambda: not slist.showing) _finish(app, delete, None) # Confirm nothing was deleted compare to the default selections browser = _open_storage_browser(app) browser.find_fuzzy("pool-dir", "table cell").click() browser.find("vol-refresh", "push button").click() browser.find("overlay.img", "table cell") browser.find("sharevol.img", "table cell") def testDeleteDeviceNoStorage(app): """ Verify successful device remove with storage doesn't touch host storage """ details = app.manager_open_details("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() delete = app.find_window("Remove Disk") chk = delete.find("Delete associated", "check box") lib.utils.check(lambda: not chk.checked) _finish(app, delete, []) details.window_close() browser = _open_storage_browser(app) browser.find_fuzzy("pool-dir", "table cell").click() browser.find("vol-refresh", "push button").click() 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) 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 = "/pool-dir/overlay.img" delete.find_fuzzy(path) _finish(app, delete, [path]) details.window_close() browser = _open_storage_browser(app) browser.find_fuzzy("pool-dir", "table cell").click() browser.find("vol-refresh", "push button").click() lib.utils.check(lambda: "overlay.img" not in browser.fmt_nodes()) 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() 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 = "/pool-dir/overlay.img" delete.find_fuzzy(path) _finish(app, delete, [path], expect_fail=True) app.click_alert_button("Storage will not be.*deleted", "OK") details.window_close() # Verify file still exists browser = _open_storage_browser(app) browser.find_fuzzy("pool-dir", "table cell").click() browser.find("vol-refresh", "push button").click() browser.find("overlay.img", "table cell")