tests: add search based on window class to wait_for_window

Searching based on class is used in many tests, searching by class, not
only by name in wait_for_window will allow to reduce code duplication.
While at it, improve it additionally:
 - avoid active waiting for window and use `xdotool search --sync` instead
 - return found window id
 - add wait_for_window_coro() for use where coroutine is needed
 - when waiting for window to disappear, check window id once and wait
   for that particular window to disappear (avoid xdotool race
   conditions on window enumeration)

Besides reducing code duplication, this also move various xdotool
imperfections handling into one place.
This commit is contained in:
Marek Marczykowski-Górecki 2018-10-15 05:08:25 +02:00
parent 3f5618dbb0
commit f1621c01e9
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724

View File

@ -977,7 +977,25 @@ class SystemTestCase(QubesTestCase):
return _QrexecPolicyContext(service, source, destination,
allow=allow, action=action)
def wait_for_window(self, title, timeout=30, show=True):
@asyncio.coroutine
def wait_for_window_hide_coro(self, title, winid, timeout=30):
"""
Wait for window do disappear
:param winid: window id
:return:
"""
wait_count = 0
while subprocess.call(['xdotool', 'getwindowname', str(winid)],
stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) == 0:
wait_count += 1
if wait_count > timeout * 10:
self.fail("Timeout while waiting for {}({}) window to "
"disappear".format(title, winid))
yield from asyncio.sleep(0.1)
@asyncio.coroutine
def wait_for_window_coro(self, title, search_class=False, timeout=30,
show=True):
"""
Wait for a window with a given title. Depending on show parameter,
it will wait for either window to show or to disappear.
@ -986,19 +1004,59 @@ class SystemTestCase(QubesTestCase):
:param timeout: timeout of the operation, in seconds
:param show: if True - wait for the window to be visible,
otherwise - to not be visible
:return: None
:param search_class: search based on window class instead of title
:return: window id of found window, if show=True
"""
wait_count = 0
while subprocess.call(['xdotool', 'search', '--name', title],
stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) \
!= int(not show):
wait_count += 1
if wait_count > timeout*10:
self.fail("Timeout while waiting for {} window to {}".format(
title, "show" if show else "hide")
)
self.loop.run_until_complete(asyncio.sleep(0.1))
xdotool_search = ['xdotool', 'search', '--onlyvisible']
if search_class:
xdotool_search.append('--class')
else:
xdotool_search.append('--name')
if show:
xdotool_search.append('--sync')
if not show:
try:
winid = subprocess.check_output(xdotool_search + [title],
stderr=subprocess.DEVNULL).decode()
except subprocess.CalledProcessError:
# already gone
return
yield from self.wait_for_window_hide_coro(winid, title,
timeout=timeout)
return
winid = None
while not winid:
p = yield from asyncio.create_subprocess_exec(
*xdotool_search, title,
stderr=subprocess.DEVNULL, stdout=subprocess.PIPE)
try:
(winid, _) = yield from asyncio.wait_for(
p.communicate(), timeout)
# don't check exit code, getting winid on stdout is enough
# indicator of success; specifically ignore xdotool failing
# with BadWindow or such - when some window appears only for a
# moment by xdotool didn't manage to get its properties
except asyncio.TimeoutError:
self.fail(
"Timeout while waiting for {} window to show".format(title))
return winid.decode().strip()
def wait_for_window(self, *args, **kwargs):
"""
Wait for a window with a given title. Depending on show parameter,
it will wait for either window to show or to disappear.
:param title: title of the window to wait for
:param timeout: timeout of the operation, in seconds
:param show: if True - wait for the window to be visible,
otherwise - to not be visible
:param search_class: search based on window class instead of title
:return: window id of found window, if show=True
"""
return self.loop.run_until_complete(
self.wait_for_window_coro(*args, **kwargs))
def enter_keys_in_window(self, title, keys):
"""