15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Runs the test with xvfb on linux. Runs the test normally on other platforms.
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)For simplicity in gyp targets, this script just runs the test normal on
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)non-linux platforms.
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import platform
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import signal
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import subprocess
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import test_env
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def kill(pid):
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Kills a process and traps exception if the process doesn't exist anymore.
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If the process doesn't exist, it raises an exception that we can ignore.
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    os.kill(pid, signal.SIGKILL)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except OSError:
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pass
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def get_xvfb_path(server_dir):
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Figures out which X server to use."""
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  xvfb_path = os.path.join(server_dir, 'Xvfb.' + platform.architecture()[0])
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if not os.path.exists(xvfb_path):
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    xvfb_path = os.path.join(server_dir, 'Xvfb')
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if not os.path.exists(xvfb_path):
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print >> sys.stderr, (
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'No Xvfb found in designated server path: %s' % server_dir)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise Exception('No virtual server')
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return xvfb_path
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def start_xvfb(xvfb_path, display):
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Starts a virtual X server that we run the tests in.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This makes it so we can run the tests even if we didn't start the tests from
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  an X session.
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    xvfb_path: Path to Xvfb.
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
5203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  cmd = [xvfb_path, display, '-screen', '0', '1024x768x24', '-ac',
5303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)         '-nolisten', 'tcp']
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    proc = subprocess.Popen(
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except OSError:
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print >> sys.stderr, 'Failed to run %s' % ' '.join(cmd)
59116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return
60116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return proc
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def wait_for_xvfb(xdisplaycheck, env):
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Waits for xvfb to be fully initialized by using xdisplaycheck."""
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
66116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    _logs = subprocess.check_output(
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        [xdisplaycheck],
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        stderr=subprocess.STDOUT,
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        env=env)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except OSError:
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print >> sys.stderr, 'Failed to load %s with cwd=%s' % (
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        xdisplaycheck, os.getcwd())
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return False
74116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  except subprocess.CalledProcessError as e:
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print >> sys.stderr, (
76116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        'Xvfb failed to load properly (code %d) according to %s' %
77116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        (e.returncode, xdisplaycheck))
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return False
79116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return True
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def run_executable(cmd, build_dir, env):
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Runs an executable within a xvfb buffer on linux or normally on other
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  platforms.
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  Requires that both xvfb and openbox are installed on linux.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Detects recursion with an environment variable and do not create a recursive X
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  buffer if present.
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # First look if we are inside a display.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if env.get('_CHROMIUM_INSIDE_XVFB') == '1':
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # No need to recurse.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return test_env.run_executable(cmd, env)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pid = None
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  xvfb = 'Xvfb'
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if sys.platform == 'linux2':
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Defaults to X display 9.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      display = ':9'
103116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      xvfb_proc = start_xvfb(xvfb, display)
104116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      if not xvfb_proc or not xvfb_proc.pid:
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return 1
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      env['DISPLAY'] = display
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not wait_for_xvfb(os.path.join(build_dir, 'xdisplaycheck'), env):
108116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        rc = xvfb_proc.poll()
109116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        if rc is None:
110116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          print 'Xvfb still running, stopping.'
111116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          xvfb_proc.terminate()
112116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        else:
113116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          print 'Xvfb exited, code %d' % rc
114116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
115116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        print 'Xvfb output:'
116116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        for l in xvfb_proc.communicate()[0].splitlines():
117116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          print '> %s' % l
118116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return 3
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Inhibit recursion.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      env['_CHROMIUM_INSIDE_XVFB'] = '1'
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Some ChromeOS tests need a window manager. Technically, it could be
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # another script but that would be overkill.
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try:
125f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        wm_cmd = ['openbox']
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        subprocess.Popen(
127f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            wm_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      except OSError:
129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        print >> sys.stderr, 'Failed to run %s' % ' '.join(wm_cmd)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return 1
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return test_env.run_executable(cmd, env)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  finally:
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if pid:
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kill(pid)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def main():
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(sys.argv) < 3:
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print >> sys.stderr, (
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'Usage: xvfb.py [path to build_dir] [command args...]')
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 2
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return run_executable(sys.argv[2:], sys.argv[1], os.environ.copy())
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == "__main__":
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sys.exit(main())
147