Skip to content

Commit

Permalink
tests: add search based on window class to wait_for_window
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
marmarek committed Oct 15, 2018
1 parent 3f5618d commit f1621c0
Showing 1 changed file with 70 additions and 12 deletions.
82 changes: 70 additions & 12 deletions qubes/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,25 @@ def qrexec_policy(self, service, source, destination, allow=True,
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.
Expand All @@ -986,19 +1004,59 @@ def wait_for_window(self, title, timeout=30, show=True):
: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):
"""
Expand Down

0 comments on commit f1621c0

Please sign in to comment.