1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Runs the test with xvfb on linux. Runs the test normally on other platforms.
7
8For simplicity in gyp targets, this script just runs the test normal on
9non-linux platforms.
10"""
11
12import os
13import platform
14import signal
15import subprocess
16import sys
17
18import test_env
19
20
21def kill(pid):
22  """Kills a process and traps exception if the process doesn't exist anymore.
23  """
24  # If the process doesn't exist, it raises an exception that we can ignore.
25  try:
26    os.kill(pid, signal.SIGKILL)
27  except OSError:
28    pass
29
30
31def get_xvfb_path(server_dir):
32  """Figures out which X server to use."""
33  xvfb_path = os.path.join(server_dir, 'Xvfb.' + platform.architecture()[0])
34  if not os.path.exists(xvfb_path):
35    xvfb_path = os.path.join(server_dir, 'Xvfb')
36  if not os.path.exists(xvfb_path):
37    print >> sys.stderr, (
38        'No Xvfb found in designated server path: %s' % server_dir)
39    raise Exception('No virtual server')
40  return xvfb_path
41
42
43def start_xvfb(xvfb_path, display):
44  """Starts a virtual X server that we run the tests in.
45
46  This makes it so we can run the tests even if we didn't start the tests from
47  an X session.
48
49  Args:
50    xvfb_path: Path to Xvfb.
51  """
52  cmd = [xvfb_path, display, '-screen', '0', '1024x768x24', '-ac',
53         '-nolisten', 'tcp']
54  try:
55    proc = subprocess.Popen(
56        cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
57  except OSError:
58    print >> sys.stderr, 'Failed to run %s' % ' '.join(cmd)
59    return
60  return proc
61
62
63def wait_for_xvfb(xdisplaycheck, env):
64  """Waits for xvfb to be fully initialized by using xdisplaycheck."""
65  try:
66    _logs = subprocess.check_output(
67        [xdisplaycheck],
68        stderr=subprocess.STDOUT,
69        env=env)
70  except OSError:
71    print >> sys.stderr, 'Failed to load %s with cwd=%s' % (
72        xdisplaycheck, os.getcwd())
73    return False
74  except subprocess.CalledProcessError as e:
75    print >> sys.stderr, (
76        'Xvfb failed to load properly (code %d) according to %s' %
77        (e.returncode, xdisplaycheck))
78    return False
79
80  return True
81
82
83def run_executable(cmd, build_dir, env):
84  """Runs an executable within a xvfb buffer on linux or normally on other
85  platforms.
86
87  Requires that both xvfb and openbox are installed on linux.
88
89  Detects recursion with an environment variable and do not create a recursive X
90  buffer if present.
91  """
92  # First look if we are inside a display.
93  if env.get('_CHROMIUM_INSIDE_XVFB') == '1':
94    # No need to recurse.
95    return test_env.run_executable(cmd, env)
96
97  pid = None
98  xvfb = 'Xvfb'
99  try:
100    if sys.platform == 'linux2':
101      # Defaults to X display 9.
102      display = ':9'
103      xvfb_proc = start_xvfb(xvfb, display)
104      if not xvfb_proc or not xvfb_proc.pid:
105        return 1
106      env['DISPLAY'] = display
107      if not wait_for_xvfb(os.path.join(build_dir, 'xdisplaycheck'), env):
108        rc = xvfb_proc.poll()
109        if rc is None:
110          print 'Xvfb still running, stopping.'
111          xvfb_proc.terminate()
112        else:
113          print 'Xvfb exited, code %d' % rc
114
115        print 'Xvfb output:'
116        for l in xvfb_proc.communicate()[0].splitlines():
117          print '> %s' % l
118
119        return 3
120      # Inhibit recursion.
121      env['_CHROMIUM_INSIDE_XVFB'] = '1'
122      # Some ChromeOS tests need a window manager. Technically, it could be
123      # another script but that would be overkill.
124      try:
125        wm_cmd = ['openbox']
126        subprocess.Popen(
127            wm_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env)
128      except OSError:
129        print >> sys.stderr, 'Failed to run %s' % ' '.join(wm_cmd)
130        return 1
131    return test_env.run_executable(cmd, env)
132  finally:
133    if pid:
134      kill(pid)
135
136
137def main():
138  if len(sys.argv) < 3:
139    print >> sys.stderr, (
140        'Usage: xvfb.py [path to build_dir] [command args...]')
141    return 2
142  return run_executable(sys.argv[2:], sys.argv[1], os.environ.copy())
143
144
145if __name__ == "__main__":
146  sys.exit(main())
147