mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-02-25 18:55:27 -06:00
uitests: Centralize object list smoketest handling
This commit is contained in:
parent
a4e1b64f1a
commit
570ee90c4d
@ -1,6 +1,3 @@
|
||||
import dogtail.rawinput
|
||||
import pyatspi
|
||||
|
||||
from tests.uitests import utils as uiutils
|
||||
|
||||
|
||||
@ -32,48 +29,8 @@ class Details(uiutils.UITestCase):
|
||||
HW panel shows itself without raising any error.
|
||||
"""
|
||||
win = self._open_details_window()
|
||||
|
||||
# Ensure the Overview page is the first selected
|
||||
win.find_pattern("Hypervisor Details", "label")
|
||||
win.find_pattern("Overview", "table cell").click()
|
||||
|
||||
# After we hit this number of down presses, start checking for
|
||||
# widget focus to determine if we hit the end of the list. We
|
||||
# don't check for widget focus unconditionally because it's slow.
|
||||
# The seemingly arbitrary number here is because it matches the
|
||||
# number of devices in test-many-devices at the time of this writing.
|
||||
check_after = 93
|
||||
|
||||
focused = None
|
||||
old_focused = None
|
||||
count = 0
|
||||
while True:
|
||||
count += 1
|
||||
dogtail.rawinput.pressKey("Down")
|
||||
|
||||
if not win.getState().contains(pyatspi.STATE_ACTIVE):
|
||||
# Should mean an error dialog popped up
|
||||
self.app.root.find_pattern("Error", "alert")
|
||||
raise AssertionError(
|
||||
"One of the hardware pages raised an error")
|
||||
|
||||
if count < check_after:
|
||||
#time.sleep(.05)
|
||||
continue
|
||||
|
||||
# pylint: disable=not-an-iterable
|
||||
old_focused = focused
|
||||
focused = win.focused_nodes()
|
||||
if old_focused is None:
|
||||
continue
|
||||
|
||||
overlap = [w for w in old_focused if w in focused]
|
||||
if len(overlap) == len(old_focused):
|
||||
# Focus didn't change, meaning we hit the end of the HW list,
|
||||
# so our testing is done
|
||||
break
|
||||
|
||||
return
|
||||
lst = win.find_pattern("hw-list", "table")
|
||||
self._walkUIList(win, lst, lambda: False)
|
||||
|
||||
def _testRename(self, origname, newname):
|
||||
win = self._open_details_window(origname)
|
||||
|
@ -1,8 +1,3 @@
|
||||
import time
|
||||
|
||||
import dogtail.rawinput
|
||||
import pyatspi
|
||||
|
||||
from tests.uitests import utils as uiutils
|
||||
|
||||
|
||||
@ -25,39 +20,6 @@ class Host(uiutils.UITestCase):
|
||||
win.find_fuzzy(tab, "page tab").click()
|
||||
return win
|
||||
|
||||
def _checkListEntrys(self, win, check_after):
|
||||
# After we hit this number of down presses, start checking for
|
||||
# widget focus to determine if we hit the end of the list. We
|
||||
# don't check for widget focus unconditionally because it's slow.
|
||||
focused = None
|
||||
old_focused = None
|
||||
count = 0
|
||||
while True:
|
||||
count += 1
|
||||
dogtail.rawinput.pressKey("Down")
|
||||
|
||||
if not win.getState().contains(pyatspi.STATE_ACTIVE):
|
||||
# Should mean an error dialog popped up
|
||||
self.app.root.find_pattern("Error", "alert")
|
||||
raise AssertionError(
|
||||
"One of the pages raised an error")
|
||||
|
||||
if count < check_after:
|
||||
time.sleep(.1)
|
||||
continue
|
||||
|
||||
# pylint: disable=not-an-iterable
|
||||
old_focused = focused
|
||||
focused = win.focused_nodes()
|
||||
if old_focused is None:
|
||||
continue
|
||||
|
||||
overlap = [w for w in old_focused if w in focused]
|
||||
if len(overlap) == len(old_focused):
|
||||
# Focus didn't change, meaning we hit the end of the HW list,
|
||||
# so our testing is done
|
||||
break
|
||||
|
||||
|
||||
##############
|
||||
# Test cases #
|
||||
@ -68,33 +30,24 @@ class Host(uiutils.UITestCase):
|
||||
Verify that each virtual network displays, without error.
|
||||
"""
|
||||
win = self._open_host_window("Virtual Networks")
|
||||
|
||||
# Make sure the first item is selected
|
||||
cell = win.find_pattern("default", "table cell")
|
||||
self.assertTrue(cell.getState().contains(pyatspi.STATE_SELECTED))
|
||||
|
||||
self._checkListEntrys(win, 13)
|
||||
lst = win.find_pattern("net-list", "table")
|
||||
errlabel = win.find_pattern("net-error-label", "label")
|
||||
self._walkUIList(win, lst, lambda: errlabel.showing)
|
||||
|
||||
def testHostStorageSmokeTest(self):
|
||||
"""
|
||||
Verify that each storage pool displays, without error.
|
||||
"""
|
||||
win = self._open_host_window("Storage")
|
||||
|
||||
# Make sure the first item is selected
|
||||
cell = win.find_pattern("cross-pool", "table cell")
|
||||
self.assertTrue(cell.getState().contains(pyatspi.STATE_SELECTED))
|
||||
|
||||
self._checkListEntrys(win, 13)
|
||||
lst = win.find_pattern("pool-list", "table")
|
||||
errlabel = win.find_pattern("pool-error-label", "label")
|
||||
self._walkUIList(win, lst, lambda: errlabel.showing)
|
||||
|
||||
def testHostInterfaceSmokeTest(self):
|
||||
"""
|
||||
Verify that each storage pool displays, without error.
|
||||
Verify that each interface displays, without error.
|
||||
"""
|
||||
win = self._open_host_window("Network Interfaces")
|
||||
|
||||
# Make sure the first item is selected
|
||||
cell = win.find_pattern("bond0", "table cell")
|
||||
self.assertTrue(cell.getState().contains(pyatspi.STATE_SELECTED))
|
||||
|
||||
self._checkListEntrys(win, 18)
|
||||
lst = win.find_pattern("interface-list", "table")
|
||||
errlabel = win.find_pattern("interface-error-label", "label")
|
||||
self._walkUIList(win, lst, lambda: errlabel.showing)
|
||||
|
@ -24,6 +24,37 @@ class UITestCase(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
self.app.stop()
|
||||
|
||||
def _walkUIList(self, win, lst, error_cb):
|
||||
"""
|
||||
Toggle down through a UI list like addhardware, net/storage/iface
|
||||
lists, and ensure an error isn't raised.
|
||||
"""
|
||||
# Walk the lst UI and find all labelled table cells, these are
|
||||
# the actual list entries
|
||||
all_cells = lst.findChildren(lambda w: w.roleName == "table cell")
|
||||
all_cells[0].click()
|
||||
cells_per_selection = len([c for c in all_cells if c.focused])
|
||||
|
||||
idx = 0
|
||||
while idx < len(all_cells):
|
||||
cell = all_cells[idx]
|
||||
self.assertTrue(cell.state_selected)
|
||||
dogtail.rawinput.pressKey("Down")
|
||||
|
||||
if not win.active:
|
||||
# Should mean an error dialog popped up
|
||||
self.app.root.find_pattern("Error", "alert")
|
||||
raise AssertionError("Error dialog raised?")
|
||||
if error_cb():
|
||||
raise AssertionError("Error found on a page")
|
||||
|
||||
idx += cells_per_selection
|
||||
if idx >= len(all_cells):
|
||||
# Last cell, selection shouldn't have changed
|
||||
self.assertTrue(cell.state_selected)
|
||||
else:
|
||||
self.assertTrue(not cell.state_selected)
|
||||
|
||||
|
||||
class _FuzzyPredicate(dogtail.predicate.Predicate):
|
||||
"""
|
||||
@ -43,7 +74,7 @@ class _FuzzyPredicate(dogtail.predicate.Predicate):
|
||||
def describeSearchResult(self, node=None):
|
||||
if not node:
|
||||
return ""
|
||||
return node_string(node)
|
||||
return node.node_string()
|
||||
|
||||
def satisfiedByNode(self, node):
|
||||
"""
|
||||
@ -93,6 +124,10 @@ class VMMDogtailNode(dogtail.tree.Node):
|
||||
"""
|
||||
return self.getState().contains(pyatspi.STATE_ACTIVE)
|
||||
|
||||
@property
|
||||
def state_selected(self):
|
||||
return self.getState().contains(pyatspi.STATE_SELECTED)
|
||||
|
||||
|
||||
#########################
|
||||
# Widget search helpers #
|
||||
@ -147,7 +182,7 @@ class VMMDogtailNode(dogtail.tree.Node):
|
||||
return msg
|
||||
|
||||
|
||||
def print_nodes(root):
|
||||
def print_nodes(self):
|
||||
"""
|
||||
Helper to print the entire node tree for the passed root. Useful
|
||||
if to figure out the roleName for the object you are looking for
|
||||
@ -160,19 +195,6 @@ class VMMDogtailNode(dogtail.tree.Node):
|
||||
|
||||
self.findChildren(_walk, isLambda=True)
|
||||
|
||||
def focused_nodes(self):
|
||||
"""
|
||||
Return a list of all focused nodes. Useful for debugging
|
||||
"""
|
||||
def _walk(node):
|
||||
try:
|
||||
if node.focused:
|
||||
return node
|
||||
except Exception as e:
|
||||
print("got exception: %s" % e)
|
||||
|
||||
return self.findChildren(_walk, isLambda=True)
|
||||
|
||||
|
||||
# This is the same hack dogtail uses to extend the Accessible class.
|
||||
_bases = list(pyatspi.Accessibility.Accessible.__bases__)
|
||||
|
28
ui/host.ui
28
ui/host.ui
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.20.0 -->
|
||||
<!-- Generated with glade 3.20.2 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.14"/>
|
||||
<object class="GtkAccelGroup" id="accelgroup1"/>
|
||||
@ -391,11 +391,12 @@
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="net-list">
|
||||
<property name="width_request">134</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">False</property>
|
||||
@ -404,6 +405,11 @@
|
||||
<signal name="changed" handler="on_net_list_changed" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="net-list-atkobject">
|
||||
<property name="AtkObject::accessible-name">net-list</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@ -1238,6 +1244,11 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">some error here</property>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="network-error-label-atkobject">
|
||||
<property name="AtkObject::accessible-name">net-error-label</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
@ -1474,6 +1485,11 @@
|
||||
<signal name="changed" handler="on_interface_list_changed" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="interface-list-atkobject">
|
||||
<property name="AtkObject::accessible-name">interface-list</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@ -1904,6 +1920,11 @@
|
||||
<property name="label">some message
|
||||
here</property>
|
||||
<property name="justify">center</property>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="interface-error-label-atkobject">
|
||||
<property name="AtkObject::accessible-name">interface-error-label</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
@ -2096,6 +2117,9 @@ here</property>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="image3">
|
||||
<property name="visible">True</property>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.20.0 -->
|
||||
<!-- Generated with glade 3.20.2 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.14"/>
|
||||
<object class="GtkImage" id="image3">
|
||||
@ -231,6 +231,11 @@
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection"/>
|
||||
</child>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="pool-list-atkobject">
|
||||
<property name="AtkObject::accessible-name">pool-list</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@ -577,6 +582,11 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">some error here</property>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="storage-error-label-atkobject">
|
||||
<property name="AtkObject::accessible-name">pool-error-label</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
|
Loading…
Reference in New Issue
Block a user