summaryrefslogtreecommitdiffstats
path: root/testing/xvfb.py
blob: c6e9ddfb74520c6f204e0477b85db0c19b7fbf09 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Runs tests with Xvfb and Openbox on Linux and normally on other platforms."""

import os
import platform
import signal
import subprocess
import sys

import test_env


def kill(proc):
  """Kills |proc| and ignores exceptions thrown for non-existent processes."""
  try:
    if proc and proc.pid:
      os.kill(proc.pid, signal.SIGKILL)
  except OSError:
    pass


def wait_for_xvfb(xdisplaycheck, env):
  """Waits for xvfb to be fully initialized by using xdisplaycheck."""
  try:
    subprocess.check_output([xdisplaycheck], stderr=subprocess.STDOUT, env=env)
  except OSError:
    print >> sys.stderr, 'Failed to load %s with cwd=%s' % (
        xdisplaycheck, os.getcwd())
    return False
  except subprocess.CalledProcessError as e:
    print >> sys.stderr, ('Xvfb failed to load (code %d) according to %s' %
                          (e.returncode, xdisplaycheck))
    return False

  return True


def should_start_xvfb(env):
  """Xvfb is only used on Linux and shouldn't be invoked recursively."""
  return sys.platform == 'linux2' and env.get('_CHROMIUM_INSIDE_XVFB') != '1'


def start_xvfb(env, build_dir, xvfb_path='Xvfb', display=':9'):
  """Start a virtual X server that can run tests without an existing X session.

  Returns the Xvfb and Openbox process Popen objects, or None on failure.
  The |env| dictionary is modified to set the DISPLAY and prevent re-entry.

  Args:
    env:       The os.environ dictionary [copy] to check for re-entry.
    build_dir: The path of the build directory, used for xdisplaycheck.
    xvfb_path: The path to Xvfb.
    display:   The X display number to use.
  """
  assert should_start_xvfb(env)
  assert env.get('_CHROMIUM_INSIDE_XVFB') != '1'
  env['_CHROMIUM_INSIDE_XVFB'] = '1'
  env['DISPLAY'] = display
  xvfb_proc = None
  openbox_proc = None

  try:
    xvfb_cmd = [xvfb_path, display, '-screen', '0', '1024x768x24', '-ac',
                '-nolisten', 'tcp', '-dpi', '96']
    xvfb_proc = subprocess.Popen(xvfb_cmd, stdout=subprocess.PIPE,
                                 stderr=subprocess.STDOUT)

    if not wait_for_xvfb(os.path.join(build_dir, 'xdisplaycheck'), env):
      rc = xvfb_proc.poll()
      if rc is None:
        print 'Xvfb still running after xdisplaycheck failure, stopping.'
        kill(xvfb_proc)
      else:
        print 'Xvfb exited (code %d) after xdisplaycheck failure.' % rc
      print 'Xvfb output:'
      for l in xvfb_proc.communicate()[0].splitlines():
        print '> %s' % l
      return None

    # Some ChromeOS tests need a window manager.
    openbox_proc = subprocess.Popen('openbox', stdout=subprocess.PIPE,
                                    stderr=subprocess.STDOUT, env=env)
  except OSError as e:
    print >> sys.stderr, 'Failed to start Xvfb or Openbox: %s' % str(e)
    kill(xvfb_proc)
    kill(openbox_proc)
    return (None, None)

  return (xvfb_proc, openbox_proc)


def run_executable(cmd, build_dir, env):
  """Runs an executable within Xvfb on Linux or normally on other platforms.

  Returns the exit code of the specified commandline, or 1 on failure.
  """
  xvfb = None
  openbox = None
  if should_start_xvfb(env):
    (xvfb, openbox) = start_xvfb(env, build_dir)
    if not xvfb or not xvfb.pid or not openbox or not openbox.pid:
      return 1
  try:
    return test_env.run_executable(cmd, env)
  finally:
    kill(xvfb)
    kill(openbox)


def main():
  if len(sys.argv) < 3:
    print >> sys.stderr, (
        'Usage: xvfb.py [path to build_dir] [command args...]')
    return 2
  return run_executable(sys.argv[2:], sys.argv[1], os.environ.copy())


if __name__ == "__main__":
  sys.exit(main())