diff --git a/tests/data/testdriver/testdriver.xml b/tests/data/testdriver/testdriver.xml index 43a7b9222..d77b650b4 100644 --- a/tests/data/testdriver/testdriver.xml +++ b/tests/data/testdriver/testdriver.xml @@ -753,6 +753,16 @@ test-many-devices, like an alternate RNG, EOL OS ID, title field + + + + + + + + + + diff --git a/tests/uitests/data/keyfile/allstats.ini b/tests/uitests/data/keyfile/allstats.ini index d85dbe827..e971374e6 100644 --- a/tests/uitests/data/keyfile/allstats.ini +++ b/tests/uitests/data/keyfile/allstats.ini @@ -6,7 +6,7 @@ host-cpu-usage=true memory-usage=true [org/virt-manager/virt-manager/connections] -uris=['test:///default', 'foo:///'] +uris=['test:///default'] autoconnect=['test:///default'] [org/virt-manager/virt-manager] diff --git a/tests/uitests/test_cli.py b/tests/uitests/test_cli.py index 44c98699c..f036ed697 100644 --- a/tests/uitests/test_cli.py +++ b/tests/uitests/test_cli.py @@ -107,5 +107,18 @@ class VMMCLI(uiutils.UITestCase): uiutils.check(lambda: not self.app.is_running()) def testCLITraceLibvirt(self): - self.app.open(extra_opts=["--trace-libvirt=mainloop"]) - self.sleep(5) + # 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.sleep(2) + uiutils.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.sleep(2) + # Give it a little time to work + uiutils.check(lambda: self.app.topwin.active) + self.app.topwin.keyCombo("F4") diff --git a/tests/uitests/test_createconn.py b/tests/uitests/test_createconn.py index 65ef19c0e..6a27cf2fb 100644 --- a/tests/uitests/test_createconn.py +++ b/tests/uitests/test_createconn.py @@ -17,8 +17,7 @@ class VMMConnect(uiutils.UITestCase): # 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() + self.app.root.find("conn-disconnect", "menu item").click() uiutils.check(lambda: "Not Connected" in c.text) c.click(button=3) self.app.root.find("conn-delete", "menu item").click() @@ -138,3 +137,8 @@ class VMMConnect(uiutils.UITestCase): c.doubleClick() c = self.app.root.find("test default", "table cell") c.click() + # Delete it + c.click(button=3) + self.app.root.find("conn-delete", "menu item").click() + self._click_alert_button("will remove the connection", "Yes") + uiutils.check(lambda: c.dead) diff --git a/tests/uitests/test_createvm.py b/tests/uitests/test_createvm.py index df9e88ea5..12babd893 100644 --- a/tests/uitests/test_createvm.py +++ b/tests/uitests/test_createvm.py @@ -234,7 +234,6 @@ class NewVM(uiutils.UITestCase): # Change NIC mac vmwindow.find_fuzzy("NIC", "table cell").click() tab = vmwindow.find("network-tab") - tab.print_nodes() tab.find("mac-entry", "text").set_text("00:11:00:11:00:11") appl.click() uiutils.check(lambda: not appl.sensitive) @@ -307,9 +306,73 @@ class NewVM(uiutils.UITestCase): self.app.root.find_fuzzy("fedora10 on", "frame") uiutils.check(lambda: not newvm.showing) - def testNewKVMQ35(self): + def testNewKVMQ35Tweaks(self): """ - New VM that should default to Q35 + 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() + uiutils.check(lambda: not appl.sensitive) + details.combo_select("Chipset:", "Q35") + appl.click() + uiutils.check(lambda: not appl.sensitive) + # Switch to UEFI, back to BIOS, back to UEFI + details.combo_select("Firmware:", ".*x86_64.*") + appl.click() + uiutils.check(lambda: not appl.sensitive) + # Switch back to BIOS + details.combo_select("Firmware:", "BIOS") + appl.click() + uiutils.check(lambda: not appl.sensitive) + # Switch back to UEFI + details.combo_select("Firmware:", ".*x86_64.*") + appl.click() + uiutils.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) + uiutils.check(lambda: tab.showing) + addhw.find("Finish", "push button").click() + uiutils.check(lambda: not addhw.active) + uiutils.check(lambda: details.active) + + # Finish + details.find_fuzzy("Begin Installation", "button").click() + uiutils.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() @@ -331,28 +394,17 @@ class NewVM(uiutils.UITestCase): vmname = "fedora30" details = self.app.root.find_fuzzy("%s on" % vmname, "frame") - # Tweak some Overview settings + # 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() - # 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) - uiutils.check(lambda: tab.showing) - addhw.find("Finish", "push button").click() - uiutils.check(lambda: not addhw.active) - uiutils.check(lambda: details.active) - # Finish details.find_fuzzy("Begin Installation", "button").click() uiutils.check(lambda: details.dead) self.app.root.find_fuzzy("%s on" % vmname, "frame") - def testNewPPC64(self): """ New PPC64 VM to test architecture selection @@ -380,6 +432,13 @@ class NewVM(uiutils.UITestCase): 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() + uiutils.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") @@ -513,7 +572,6 @@ class NewVM(uiutils.UITestCase): # Tweak init values details.find("Boot Options", "table cell").click() tab = details.find("boot-tab") - tab.print_nodes() tab.find("Init path:", "text").set_text("") tab.find("Init args:", "text").set_text("some args") appl = details.find("config-apply") @@ -772,11 +830,28 @@ class NewVM(uiutils.UITestCase): newvm.find_fuzzy("Customize", "check").click() newvm.find_fuzzy("Name", "text").set_text(vmname) newvm.find_fuzzy("Finish", "button").click() - - # Change a VM setting and verify it 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" + uiutils.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() + uiutils.check(lambda: not appl.sensitive) + uiutils.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") @@ -833,7 +908,6 @@ class NewVM(uiutils.UITestCase): browser = self.app.root.find("vmm-storage-browser") browser.find("%s.qcow2" % vmname, "table cell") - def testNewVMRemote(self): """ Hit some is_remote code paths diff --git a/tests/uitests/test_details.py b/tests/uitests/test_details.py index a2729f786..b73c063a5 100644 --- a/tests/uitests/test_details.py +++ b/tests/uitests/test_details.py @@ -49,13 +49,18 @@ class Details(uiutils.UITestCase): # Select XML editor, and reverse walk the list win.find("XML", "page tab").click() self._walkUIList(win, lst, lambda: False, reverse=True) + return win def testDetailsHardwareSmokeTest(self): self._testSmokeTest(None) def testDetailsHardwareSmokeTestAlternate(self): self.app.open(keyfile="allstats.ini") - self._testSmokeTest("test alternate devs title") + 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.sleep(2) def _testRename(self, origname, newname): # Enable all stats prefs to hit some extra code @@ -66,8 +71,16 @@ class Details(uiutils.UITestCase): 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._click_alert_button(badname, "Close") + + # Actual name change win.find("Name:", "text").set_text(newname) - win.find("config-apply", "push button").click() + appl.click() + uiutils.check(lambda: not appl.sensitive) # Confirm lists were updated self.app.root.find("%s on" % newname, "frame") @@ -91,7 +104,6 @@ class Details(uiutils.UITestCase): self.app.root.find_fuzzy(origname, "table cell").click() b = self.app.root.find("Shut Down", "push button") b.click() - # This insures the VM finished shutting down uiutils.check(lambda: b.sensitive is False) self._testRename(origname, "test-new-name") @@ -102,7 +114,9 @@ class Details(uiutils.UITestCase): """ self.app.uri = tests.utils.URIs.kvm win = self._open_details_window(vmname="test", shutdown=True) - self.app.topwin.click_title() + fmenu = win.find("File", "menu") + fmenu.click() + fmenu.find("View Manager").click() # Double run to hit a show() codepath win = self._open_details_window(vmname="test") uiutils.check(lambda: win.active) @@ -308,6 +322,30 @@ class Details(uiutils.UITestCase): tab = self._select_hw(win, "Boot Options", "boot-tab") uiutils.check(lambda: "backing" in initrd.text) + def testDetailsAlternateEdits(self): + """ + Some specific handling via test-alternate-devs + """ + win = self._open_details_window(vmname="test alternate devs title") + + # tests the console dup removal + self._select_hw(win, "Serial 1", "char-tab") + win.find("config-remove").click() + self._click_alert_button("Are you sure", "Yes") + self._click_alert_button("take effect after", "OK") + self._stop_vm(win) + + def testDetailsEmptyBoot(self): + """ + Check boot handling when VM has no devices + """ + win = self._open_details_window(vmname="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 @@ -398,8 +436,7 @@ class Details(uiutils.UITestCase): """ Test all other devices """ - win = self._open_details_window(vmname="test-many-devices", - shutdown=True) + win = self._open_details_window(vmname="test-many-devices") appl = win.find("config-apply", "push button") # Fail to hotremove @@ -506,6 +543,10 @@ class Details(uiutils.UITestCase): tab.combo_select("controller-model", "USB 3") appl.click() uiutils.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() + uiutils.check(lambda: not appl.sensitive) # Filesystem tweaks diff --git a/tests/uitests/test_host.py b/tests/uitests/test_host.py index a4757658f..6235a3017 100644 --- a/tests/uitests/test_host.py +++ b/tests/uitests/test_host.py @@ -160,7 +160,8 @@ class Host(uiutils.UITestCase): win.find_fuzzy("Network", "tab").click() win.find_fuzzy("Overview", "tab").click() - # Unset autoconnect + # Toggle autoconnect + win.find("Autoconnect:", "check box").click() win.find("Autoconnect:", "check box").click() # Change the name, verify that title bar changed diff --git a/tests/uitests/test_inspection.py b/tests/uitests/test_inspection.py index b46941bee..8cf3d5879 100644 --- a/tests/uitests/test_inspection.py +++ b/tests/uitests/test_inspection.py @@ -42,3 +42,14 @@ class VMMInspection(uiutils.UITestCase): assert "test_app1_summary" in nodestr1 tab.find("Refresh", "push button").click() uiutils.check(lambda: apps.fmt_nodes() != nodestr1) + + details.keyCombo("F4") + uiutils.check(lambda: not details.showing) + + # 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") diff --git a/tests/uitests/test_manager.py b/tests/uitests/test_manager.py index 0d83db97e..2dda674a9 100644 --- a/tests/uitests/test_manager.py +++ b/tests/uitests/test_manager.py @@ -1,6 +1,7 @@ # This work is licensed under the GNU GPLv2 or later. # See the COPYING file in the top-level directory. +from tests import utils from tests.uitests import utils as uiutils @@ -47,6 +48,8 @@ class Manager(uiutils.UITestCase): uiutils.check(lambda: shutdown.sensitive, timeout=5) def testVMLifecycle(self): + # qemu hits some different domain code paths for setTime + self.app.uri = utils.URIs.kvm self._testVMLifecycle() def testVMNoEventsLifecycle(self): @@ -130,7 +133,7 @@ class Manager(uiutils.UITestCase): test_action("Restore", shutdown=False, confirm=False) confirm_is_running() - def testManagedManagedSaveCornerCases(self): + def testManagerSaveCancelError(self): """ Test managed save special behavior """ @@ -162,6 +165,32 @@ class Manager(uiutils.UITestCase): self._click_alert_button("remove the saved state", "Yes") uiutils.check(lambda: not run.sensitive) + def testManagerQEMUSetTime(self): + """ + Fake qemu setTime behavior for code coverage + """ + self.app.uri = 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") + + c = manager.find("test alternate devs title", "table cell") + c.click() + + # Save -> resume -> save + smenu.click() + save.click() + uiutils.check(lambda: run.sensitive) + self.sleep(1) + run.click() + uiutils.check(lambda: not run.sensitive) + self.sleep(1) + smenu.click() + save.click() + uiutils.check(lambda: run.sensitive) + self.sleep(1) + 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"]) @@ -220,6 +249,10 @@ class Manager(uiutils.UITestCase): """ manager = self.app.topwin host = self._open_host_window("Storage") + fmenu = host.find("File", "menu") + fmenu.click() + fmenu.find("View Manager", "menu item").click() + uiutils.check(lambda: manager.active) # Double click title to maximize manager.click_title() diff --git a/tests/uitests/test_snapshot.py b/tests/uitests/test_snapshot.py index a6cd55224..a04d52e9d 100644 --- a/tests/uitests/test_snapshot.py +++ b/tests/uitests/test_snapshot.py @@ -44,6 +44,13 @@ class Snapshots(uiutils.UITestCase): vmpause = win.find("Pause", "toggle button") snaprun = win.find("snapshot-start", "push button") + # Start already running snapshot + snapname = "internal-root" + win.find(snapname, "table cell").click() + snaprun.click() + self._click_alert_button("run the snapshot '%s'" % snapname, "Yes") + uiutils.check(lambda: not vmrun.sensitive) + # Start offline snapshot snapname = "offline-root" win.find(snapname, "table cell").click() diff --git a/ui/details.ui b/ui/details.ui index 56a400966..ebf4f75bb 100644 --- a/ui/details.ui +++ b/ui/details.ui @@ -569,6 +569,11 @@ False start + + + machine-combo + + False diff --git a/virtManager/config.py b/virtManager/config.py index 43f8afb21..ec123bb9e 100644 --- a/virtManager/config.py +++ b/virtManager/config.py @@ -4,8 +4,6 @@ # This work is licensed under the GNU GPLv2 or later. # See the COPYING file in the top-level directory. -import os - from gi.repository import Gio from gi.repository import GLib from gi.repository import Gtk @@ -151,18 +149,25 @@ class vmmConfig(object): "local_title": _("Locate existing storage"), "dialog_type": Gtk.FileChooserAction.SAVE, "choose_button": Gtk.STOCK_OPEN, + "gsettings_key": "image", + }, + + CONFIG_DIR_SCREENSHOT: { + "gsettings_key": "screenshot", }, CONFIG_DIR_ISO_MEDIA: { "enable_create": False, "storage_title": _("Locate ISO media volume"), "local_title": _("Locate ISO media"), + "gsettings_key": "media", }, CONFIG_DIR_FLOPPY_MEDIA: { "enable_create": False, "storage_title": _("Locate floppy media volume"), "local_title": _("Locate floppy media"), + "gsettings_key": "media", }, CONFIG_DIR_FS: { @@ -367,11 +372,9 @@ class vmmConfig(object): def on_keys_combination_changed(self, cb): return self.conf.notify_add("/console/grab-keys", cb) - # This key is not intended to be exposed in the UI yet + # This key is not intended to be exposed in the UI yet (maybe ever) def get_keyboard_grab_default(self): return self.conf.get("/console/grab-keyboard") - def set_keyboard_grab_default(self, val): - self.conf.set("/console/grab-keyboard", val) def on_keyboard_grab_default_changed(self, cb): return self.conf.notify_add("/console/grab-keyboard", cb) @@ -422,8 +425,6 @@ class vmmConfig(object): # Libguestfs VM inspection - def on_libguestfs_inspect_vms_changed(self, cb): - return self.conf.notify_add("/enable-libguestfs-vm-inspection", cb) def get_libguestfs_inspect_vms(self): if self.CLITestOptions.config_libguestfs: return True @@ -439,9 +440,7 @@ class vmmConfig(object): return 120 def get_stats_update_interval(self): interval = self.conf.get("/stats/update-interval") - if interval < 1: - return 1 - return interval + return max(interval, 1) def set_stats_update_interval(self, interval): self.conf.set("/stats/update-interval", interval) def on_stats_update_interval_changed(self, cb): @@ -482,20 +481,16 @@ class vmmConfig(object): def get_console_accels(self): console_pref = self.conf.get("/console/enable-accels") if console_pref is None: - console_pref = False + console_pref = False # pragma: no cover return console_pref def set_console_accels(self, pref): self.conf.set("/console/enable-accels", pref) - def on_console_scaling_changed(self, cb): - return self.conf.notify_add("/console/scaling", cb) def get_console_scaling(self): return self.conf.get("/console/scaling") def set_console_scaling(self, pref): self.conf.set("/console/scaling", pref) - def on_console_resizeguest_changed(self, cb): - return self.conf.notify_add("/console/resize-guest", cb) def get_console_resizeguest(self): val = self.conf.get("/console/resize-guest") if val == -1: @@ -513,21 +508,11 @@ class vmmConfig(object): def get_details_show_toolbar(self): res = self.conf.get("/details/show-toolbar") if res is None: - res = True + res = True # pragma: no cover return res def set_details_show_toolbar(self, state): self.conf.set("/details/show-toolbar", state) - # VM details default size - def get_details_window_size(self): - w = self.conf.get("/details/window_width") - h = self.conf.get("/details/window_height") - return (w, h) - def set_details_window_size(self, w, h): - self.conf.set("/details/window_width", w) - self.conf.set("/details/window_height", h) - - # New VM preferences def get_new_vm_sound(self): return self.conf.get("/new-vm/add-sound") @@ -537,7 +522,7 @@ class vmmConfig(object): def get_graphics_type(self, raw=False): ret = self.conf.get("/new-vm/graphics-type") if ret not in ["system", "vnc", "spice"]: - ret = "system" + ret = "system" # pragma: no cover if ret == "system" and not raw: return self.default_graphics_from_config return ret @@ -547,9 +532,7 @@ class vmmConfig(object): def get_add_spice_usbredir(self, raw=False): ret = self.conf.get("/new-vm/add-spice-usbredir") if ret not in ["system", "yes", "no"]: - ret = "system" - if not raw and self.get_graphics_type() != "spice": - return "no" + ret = "system" # pragma: no cover if ret == "system" and not raw: return self.default_add_spice_usbredir return ret @@ -559,7 +542,7 @@ class vmmConfig(object): def get_default_storage_format(self, raw=False): ret = self.conf.get("/new-vm/storage-format") if ret not in ["default", "raw", "qcow2"]: - ret = "default" + ret = "default" # pragma: no cover if ret == "default" and not raw: return self.default_storage_format_from_config return ret @@ -570,7 +553,7 @@ class vmmConfig(object): ret = self.conf.get("/new-vm/cpu-default") if ret not in DomainCpu.SPECIAL_MODES: - ret = DomainCpu.SPECIAL_MODE_APP_DEFAULT + ret = DomainCpu.SPECIAL_MODE_APP_DEFAULT # pragma: no cover return ret def set_default_cpu_setting(self, val): self.conf.set("/new-vm/cpu-default", val.lower()) @@ -579,15 +562,13 @@ class vmmConfig(object): # URL/Media path history def _url_add_helper(self, gsettings_path, url): maxlength = 10 - urls = self.conf.get(gsettings_path) - if urls is None: - urls = [] + urls = self.conf.get(gsettings_path) or [] if urls.count(url) == 0 and len(url) > 0 and not url.isspace(): # The url isn't already in the list, so add it urls.insert(0, url) if len(urls) > maxlength: - del urls[len(urls) - 1] + del urls[len(urls) - 1] # pragma: no cover self.conf.set(gsettings_path, urls) def add_container_url(self, url): @@ -613,7 +594,7 @@ class vmmConfig(object): current_list = self.get_perms_fix_ignore() or [] for path in pathlist: if path in current_list: - continue + continue # pragma: no cover current_list.append(path) self.conf.set("/paths/perms-fix-ignore", current_list) def get_perms_fix_ignore(self): @@ -654,9 +635,7 @@ class vmmConfig(object): return ((uris is not None) and (uri in uris)) def set_conn_autoconnect(self, uri, val): - uris = self.conf.get("/connections/autoconnect") - if uris is None: - uris = [] + uris = self.conf.get("/connections/autoconnect") or [] if not val and uri in uris: uris.remove(uri) elif val and uri not in uris: @@ -666,36 +645,23 @@ class vmmConfig(object): # Default directory location dealings - def _get_default_dir_key(self, _type): - if (_type in [self.CONFIG_DIR_ISO_MEDIA, - self.CONFIG_DIR_FLOPPY_MEDIA]): - return "media" - if (_type in [self.CONFIG_DIR_IMAGE, - self.CONFIG_DIR_SCREENSHOT]): - return _type - return None - def get_default_directory(self, conn, _type): ignore = conn - key = self._get_default_dir_key(_type) + browsedata = self.browse_reason_data.get(_type, {}) + key = browsedata.get("gsettings_key", None) path = None if key: path = self.conf.get("/paths/%s-default" % key) - if not path: - if (_type == self.CONFIG_DIR_IMAGE or - _type == self.CONFIG_DIR_ISO_MEDIA or - _type == self.CONFIG_DIR_FLOPPY_MEDIA): - path = os.getcwd() - log.debug("directory for type=%s returning=%s", _type, path) return path def set_default_directory(self, folder, _type): - key = self._get_default_dir_key(_type) + browsedata = self.browse_reason_data.get(_type, {}) + key = browsedata.get("gsettings_key", None) if not key: - return + return # pragma: no cover log.debug("saving directory for type=%s to %s", key, folder) self.conf.set("/paths/%s-default" % key, folder) diff --git a/virtManager/delete.py b/virtManager/delete.py index af97a6fc1..1d901f7c8 100644 --- a/virtManager/delete.py +++ b/virtManager/delete.py @@ -342,8 +342,7 @@ class vmmDeleteStorage(_vmmDeleteBase): # Try to hot remove detach_err = () try: - if vm.is_active(): - vm.detach_device(devobj) + vm.detach_device(devobj) except Exception as e: log.debug("Device could not be hotUNplugged: %s", str(e)) detach_err = (str(e), "".join(traceback.format_exc())) diff --git a/virtManager/details/details.py b/virtManager/details/details.py index 8a9f32539..65c7fa558 100644 --- a/virtManager/details/details.py +++ b/virtManager/details/details.py @@ -683,7 +683,7 @@ class vmmDetails(vmmGObjectUI): warn_icon.set_tooltip_text( _("Libvirt or hypervisor does not support UEFI.")) elif not uefipaths: - warn_icon.set_tooltip_text( + warn_icon.set_tooltip_text( # pragma: no cover _("Libvirt did not detect any UEFI/OVMF firmware image " "installed on the host.")) @@ -982,7 +982,7 @@ class vmmDetails(vmmGObjectUI): pagetype = self._get_hw_row()[HW_LIST_COL_TYPE] if pagetype is None: - return + return # pragma: no cover if self.widget("config-apply").get_sensitive(): # Apply button sensitive means user is making changes, don't @@ -1046,6 +1046,10 @@ class vmmDetails(vmmGObjectUI): self.vm, self.err, devobj) if not success: return + + # This call here means when the vm config changes and triggers + # refresh event, the UI page will be updated, rather than leaving + # it untouched because it thinks changes are in progress self._disable_apply() def _remove_disk(self, disk): @@ -1233,7 +1237,7 @@ class vmmDetails(vmmGObjectUI): if new_idx < 0 or new_idx >= len(boot_order): # Somehow we went out of bounds - return + return # pragma: no cover boot_list = self.widget("boot-list") model = boot_list.get_model() @@ -1814,19 +1818,17 @@ class vmmDetails(vmmGObjectUI): # Machine settings machtype = self.vm.get_machtype() or _("Unknown") + self.widget("machine-type-label").set_text(machtype) if self.widget("machine-type").is_visible(): uiutil.set_list_selection( self.widget("machine-type"), machtype) - elif self.widget("machine-type-label").is_visible(): - self.widget("machine-type-label").set_text(machtype) # Chipset chipset = _chipset_label_from_machine(machtype) + self.widget("overview-chipset-label").set_text(chipset) if self.widget("overview-chipset").is_visible(): uiutil.set_list_selection( self.widget("overview-chipset"), chipset) - elif self.widget("overview-chipset-label").is_visible(): - self.widget("overview-chipset-label").set_text(chipset) def _refresh_os_page(self): self._os_list.select_os(self.vm.xmlobj.osinfo) @@ -2165,7 +2167,7 @@ class vmmDetails(vmmGObjectUI): typelabel = _("Console Device") elif char_type == "channel": typelabel = _("Channel Device") - else: + else: # pragma: no cover typelabel = _("%s Device") % char_type.capitalize() if (target_port is not None and diff --git a/virtManager/lib/inspection.py b/virtManager/lib/inspection.py index a93aa6ae3..2e67b6381 100644 --- a/virtManager/lib/inspection.py +++ b/virtManager/lib/inspection.py @@ -19,10 +19,13 @@ def _inspection_error(_errstr): return data -def _make_fake_data(): +def _make_fake_data(vm): """ Return fake vmmInspectionData for use with the test driver """ + if not vm.xmlobj.devices.disk: + return _inspection_error("Fake test error no disks") + data = vmmInspectionData() data.os_type = "test_os_type" data.distro = "test_distro" @@ -324,7 +327,7 @@ class vmmInspection(vmmGObject): return _inspection_error( _("Cannot inspect VM on remote connection")) if conn.is_test(): - return _make_fake_data() + return _make_fake_data(vm) return _perform_inspection(conn, vm) diff --git a/virtManager/object/domain.py b/virtManager/object/domain.py index bc5ad6ecc..feccfc6ad 100644 --- a/virtManager/object/domain.py +++ b/virtManager/object/domain.py @@ -276,8 +276,9 @@ class _vmmDomainSetTimeThread(vmmGObject): # Only run the API for qemu and test drivers, they are the only ones # that support it. This will save spamming logs with error output. - if not self._domain.conn.is_qemu() and not self._domain.conn.is_test(): - return + if (not self._domain.conn.is_qemu() and + not self._domain.conn.is_test()): + return # pragma: no cover # For qemu, only run the API if the VM has the qemu guest agent in # the XML. @@ -304,30 +305,32 @@ class _vmmDomainSetTimeThread(vmmGObject): self._thread = None self._do_cancel.clear() + def _wait_for_agent(self): + # Setting time of a qemu domain can only work if an agent is + # defined and online. We only get here if one is defined. So wait + # for it to come online now. + waited = 0 + while waited < self._maxwait and not self._domain.agent_ready(): + if waited == 0: + log.debug("Waiting for qemu guest agent to come online...") + + # sleep some time and potentially abort + if self._do_cancel.wait(self._sleep): + return + + waited += self._sleep + + if not self._domain.agent_ready(): # pragma: no cover + log.debug("Giving up on qemu guest agent for time sync") + return + def _do_loop(self): """ Run the domain's set time operation. Potentially wait for a guest agent to come online beforehand. """ if self._domain.conn.is_qemu(): - # Setting time of a qemu domain can only work if an agent is - # defined and online. We only get here if one is defined. So wait - # for it to come online now. - waited = 0 - while waited < self._maxwait and not self._domain.agent_ready(): - if waited == 0: - log.debug("Waiting for qemu guest agent to come online...") - - # sleep some time and potentially abort - if self._do_cancel.wait(self._sleep): - return - - waited += self._sleep - - if not self._domain.agent_ready(): - log.debug("Giving up on qemu guest agent for time sync") - return - + self._wait_for_agent() self._domain.set_time() def _cleanup(self): @@ -369,6 +372,8 @@ class vmmDomain(vmmLibvirtObject): for snap in self._snapshot_list or []: snap.cleanup() self._snapshot_list = None + self._set_time_thread.cleanup() + self._set_time_thread = None vmmLibvirtObject._cleanup(self) def _init_libvirt_state(self): @@ -556,7 +561,7 @@ class vmmDomain(vmmLibvirtObject): if new_nvram: try: new_nvram.get_vol_object().delete(0) - except Exception as warn: + except Exception as warn: # pragma: no cover log.debug("rename failed and new nvram was not " "removed: '%s'", warn) raise error @@ -564,7 +569,7 @@ class vmmDomain(vmmLibvirtObject): if new_nvram: try: old_nvram.get_vol_object().delete(0) - except Exception as warn: + except Exception as warn: # pragma: no cover log.debug("old nvram file was not removed: '%s'", warn) self.define_overview(nvram=new_nvram.path) @@ -1059,7 +1064,9 @@ class vmmDomain(vmmLibvirtObject): def job_info(self): if self.conn.is_test(): return testmock.fake_job_info() - return self._backend.jobInfo() + # It's tough to hit this via uitests because it depends + # on the job lasting more than a second + return self._backend.jobInfo() # pragma: no cover def abort_job(self): self._backend.abortJob() @@ -1168,7 +1175,7 @@ class vmmDomain(vmmLibvirtObject): self._backend.setTime(time={"seconds": seconds, "nseconds": nseconds}) log.debug("Successfully set guest time") - except Exception as e: + except Exception as e: # pragma: no cover log.debug("Failed to set time: %s", e) def _async_set_time(self): @@ -1256,7 +1263,7 @@ class vmmDomain(vmmLibvirtObject): consoles = self.xmlobj.devices.console if not consoles: - return False + return False # pragma: no cover console = consoles[0] if (console.type == serial.type and diff --git a/virtManager/vmwindow.py b/virtManager/vmwindow.py index 9c292cef5..dab6c2085 100644 --- a/virtManager/vmwindow.py +++ b/virtManager/vmwindow.py @@ -462,8 +462,8 @@ class vmmVMWindow(vmmGObjectUI): vmmenu.VMActionUI.resume(self, self.vm) def control_vm_menu(self, src_ignore): - can_usb = bool(self.console.details_viewer_has_usb_redirection() and - self.vm.has_spicevmc_type_redirdev()) + can_usb = bool(self.vm.has_spicevmc_type_redirdev() and + self.console.details_viewer_has_usb_redirection()) self.widget("details-menu-usb-redirection").set_sensitive(can_usb) def control_vm_run(self, src_ignore):