xref: /aosp_15_r20/external/cronet/testing/xvfb.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env vpython3
2*6777b538SAndroid Build Coastguard Worker# Copyright 2012 The Chromium Authors
3*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file.
5*6777b538SAndroid Build Coastguard Worker
6*6777b538SAndroid Build Coastguard Worker"""Runs tests with Xvfb or Xorg and Openbox or Weston on Linux and normally on
7*6777b538SAndroid Build Coastguard Workerother platforms."""
8*6777b538SAndroid Build Coastguard Worker
9*6777b538SAndroid Build Coastguard Workerfrom __future__ import print_function
10*6777b538SAndroid Build Coastguard Worker
11*6777b538SAndroid Build Coastguard Workerimport copy
12*6777b538SAndroid Build Coastguard Workerimport os
13*6777b538SAndroid Build Coastguard Workerimport os.path
14*6777b538SAndroid Build Coastguard Workerimport random
15*6777b538SAndroid Build Coastguard Workerimport re
16*6777b538SAndroid Build Coastguard Workerimport signal
17*6777b538SAndroid Build Coastguard Workerimport socket
18*6777b538SAndroid Build Coastguard Workerimport subprocess
19*6777b538SAndroid Build Coastguard Workerimport sys
20*6777b538SAndroid Build Coastguard Workerimport tempfile
21*6777b538SAndroid Build Coastguard Workerimport threading
22*6777b538SAndroid Build Coastguard Workerimport time
23*6777b538SAndroid Build Coastguard Worker
24*6777b538SAndroid Build Coastguard Workerimport psutil
25*6777b538SAndroid Build Coastguard Worker
26*6777b538SAndroid Build Coastguard Workerimport test_env
27*6777b538SAndroid Build Coastguard Worker
28*6777b538SAndroid Build Coastguard WorkerDEFAULT_XVFB_WHD = '1280x800x24'
29*6777b538SAndroid Build Coastguard Worker
30*6777b538SAndroid Build Coastguard Worker# pylint: disable=useless-object-inheritance
31*6777b538SAndroid Build Coastguard Worker
32*6777b538SAndroid Build Coastguard Worker
33*6777b538SAndroid Build Coastguard Workerclass _X11ProcessError(Exception):
34*6777b538SAndroid Build Coastguard Worker  """Exception raised when Xvfb or Xorg cannot start."""
35*6777b538SAndroid Build Coastguard Worker
36*6777b538SAndroid Build Coastguard Workerclass _WestonProcessError(Exception):
37*6777b538SAndroid Build Coastguard Worker  """Exception raised when Weston cannot start."""
38*6777b538SAndroid Build Coastguard Worker
39*6777b538SAndroid Build Coastguard Worker
40*6777b538SAndroid Build Coastguard Workerdef kill(proc, name, timeout_in_seconds=10):
41*6777b538SAndroid Build Coastguard Worker  """Tries to kill |proc| gracefully with a timeout for each signal."""
42*6777b538SAndroid Build Coastguard Worker  if not proc:
43*6777b538SAndroid Build Coastguard Worker    return
44*6777b538SAndroid Build Coastguard Worker
45*6777b538SAndroid Build Coastguard Worker  thread = threading.Thread(target=proc.wait)
46*6777b538SAndroid Build Coastguard Worker  try:
47*6777b538SAndroid Build Coastguard Worker    proc.terminate()
48*6777b538SAndroid Build Coastguard Worker    thread.start()
49*6777b538SAndroid Build Coastguard Worker
50*6777b538SAndroid Build Coastguard Worker    thread.join(timeout_in_seconds)
51*6777b538SAndroid Build Coastguard Worker    if thread.is_alive():
52*6777b538SAndroid Build Coastguard Worker      print('%s running after SIGTERM, trying SIGKILL.\n' % name,
53*6777b538SAndroid Build Coastguard Worker        file=sys.stderr)
54*6777b538SAndroid Build Coastguard Worker      proc.kill()
55*6777b538SAndroid Build Coastguard Worker  except OSError as e:
56*6777b538SAndroid Build Coastguard Worker    # proc.terminate()/kill() can raise, not sure if only ProcessLookupError
57*6777b538SAndroid Build Coastguard Worker    # which is explained in https://bugs.python.org/issue40550#msg382427
58*6777b538SAndroid Build Coastguard Worker    print('Exception while killing process %s: %s' % (name, e), file=sys.stderr)
59*6777b538SAndroid Build Coastguard Worker
60*6777b538SAndroid Build Coastguard Worker  thread.join(timeout_in_seconds)
61*6777b538SAndroid Build Coastguard Worker  if thread.is_alive():
62*6777b538SAndroid Build Coastguard Worker    print('%s running after SIGTERM and SIGKILL; good luck!\n' % name,
63*6777b538SAndroid Build Coastguard Worker          file=sys.stderr)
64*6777b538SAndroid Build Coastguard Worker
65*6777b538SAndroid Build Coastguard Worker
66*6777b538SAndroid Build Coastguard Workerdef launch_dbus(env): # pylint: disable=inconsistent-return-statements
67*6777b538SAndroid Build Coastguard Worker  """Starts a DBus session.
68*6777b538SAndroid Build Coastguard Worker
69*6777b538SAndroid Build Coastguard Worker  Works around a bug in GLib where it performs operations which aren't
70*6777b538SAndroid Build Coastguard Worker  async-signal-safe (in particular, memory allocations) between fork and exec
71*6777b538SAndroid Build Coastguard Worker  when it spawns subprocesses. This causes threads inside Chrome's browser and
72*6777b538SAndroid Build Coastguard Worker  utility processes to get stuck, and this harness to hang waiting for those
73*6777b538SAndroid Build Coastguard Worker  processes, which will never terminate. This doesn't happen on users'
74*6777b538SAndroid Build Coastguard Worker  machines, because they have an active desktop session and the
75*6777b538SAndroid Build Coastguard Worker  DBUS_SESSION_BUS_ADDRESS environment variable set, but it can happen on
76*6777b538SAndroid Build Coastguard Worker  headless environments. This is fixed by glib commit [1], but this workaround
77*6777b538SAndroid Build Coastguard Worker  will be necessary until the fix rolls into Chromium's CI.
78*6777b538SAndroid Build Coastguard Worker
79*6777b538SAndroid Build Coastguard Worker  [1] f2917459f745bebf931bccd5cc2c33aa81ef4d12
80*6777b538SAndroid Build Coastguard Worker
81*6777b538SAndroid Build Coastguard Worker  Modifies the passed in environment with at least DBUS_SESSION_BUS_ADDRESS and
82*6777b538SAndroid Build Coastguard Worker  DBUS_SESSION_BUS_PID set.
83*6777b538SAndroid Build Coastguard Worker
84*6777b538SAndroid Build Coastguard Worker  Returns the pid of the dbus-daemon if started, or None otherwise.
85*6777b538SAndroid Build Coastguard Worker  """
86*6777b538SAndroid Build Coastguard Worker  if 'DBUS_SESSION_BUS_ADDRESS' in os.environ:
87*6777b538SAndroid Build Coastguard Worker    return
88*6777b538SAndroid Build Coastguard Worker  try:
89*6777b538SAndroid Build Coastguard Worker    dbus_output = subprocess.check_output(
90*6777b538SAndroid Build Coastguard Worker        ['dbus-launch'], env=env).decode('utf-8').split('\n')
91*6777b538SAndroid Build Coastguard Worker    for line in dbus_output:
92*6777b538SAndroid Build Coastguard Worker      m = re.match(r'([^=]+)\=(.+)', line)
93*6777b538SAndroid Build Coastguard Worker      if m:
94*6777b538SAndroid Build Coastguard Worker        env[m.group(1)] = m.group(2)
95*6777b538SAndroid Build Coastguard Worker    return int(env['DBUS_SESSION_BUS_PID'])
96*6777b538SAndroid Build Coastguard Worker  except (subprocess.CalledProcessError, OSError, KeyError, ValueError) as e:
97*6777b538SAndroid Build Coastguard Worker    print('Exception while running dbus_launch: %s' % e)
98*6777b538SAndroid Build Coastguard Worker
99*6777b538SAndroid Build Coastguard Worker
100*6777b538SAndroid Build Coastguard Worker# TODO(crbug.com/949194): Encourage setting flags to False.
101*6777b538SAndroid Build Coastguard Workerdef run_executable(
102*6777b538SAndroid Build Coastguard Worker    cmd, env, stdoutfile=None, use_openbox=True, use_xcompmgr=True,
103*6777b538SAndroid Build Coastguard Worker    xvfb_whd=None, cwd=None):
104*6777b538SAndroid Build Coastguard Worker  """Runs an executable within Weston, Xvfb or Xorg on Linux or normally on
105*6777b538SAndroid Build Coastguard Worker     other platforms.
106*6777b538SAndroid Build Coastguard Worker
107*6777b538SAndroid Build Coastguard Worker  The method sets SIGUSR1 handler for Xvfb to return SIGUSR1
108*6777b538SAndroid Build Coastguard Worker  when it is ready for connections.
109*6777b538SAndroid Build Coastguard Worker  https://www.x.org/archive/X11R7.5/doc/man/man1/Xserver.1.html under Signals.
110*6777b538SAndroid Build Coastguard Worker
111*6777b538SAndroid Build Coastguard Worker  Args:
112*6777b538SAndroid Build Coastguard Worker    cmd: Command to be executed.
113*6777b538SAndroid Build Coastguard Worker    env: A copy of environment variables. "DISPLAY" and will be set if Xvfb is
114*6777b538SAndroid Build Coastguard Worker      used. "WAYLAND_DISPLAY" will be set if Weston is used.
115*6777b538SAndroid Build Coastguard Worker    stdoutfile: If provided, symbolization via script is disabled and stdout
116*6777b538SAndroid Build Coastguard Worker      is written to this file as well as to stdout.
117*6777b538SAndroid Build Coastguard Worker    use_openbox: A flag to use openbox process.
118*6777b538SAndroid Build Coastguard Worker      Some ChromeOS tests need a window manager.
119*6777b538SAndroid Build Coastguard Worker    use_xcompmgr: A flag to use xcompmgr process.
120*6777b538SAndroid Build Coastguard Worker      Some tests need a compositing wm to make use of transparent visuals.
121*6777b538SAndroid Build Coastguard Worker    xvfb_whd: WxHxD to pass to xvfb or DEFAULT_XVFB_WHD if None
122*6777b538SAndroid Build Coastguard Worker    cwd: Current working directory.
123*6777b538SAndroid Build Coastguard Worker
124*6777b538SAndroid Build Coastguard Worker  Returns:
125*6777b538SAndroid Build Coastguard Worker    the exit code of the specified commandline, or 1 on failure.
126*6777b538SAndroid Build Coastguard Worker  """
127*6777b538SAndroid Build Coastguard Worker
128*6777b538SAndroid Build Coastguard Worker  # It might seem counterintuitive to support a --no-xvfb flag in a script
129*6777b538SAndroid Build Coastguard Worker  # whose only job is to start xvfb, but doing so allows us to consolidate
130*6777b538SAndroid Build Coastguard Worker  # the logic in the layers of buildbot scripts so that we *always* use
131*6777b538SAndroid Build Coastguard Worker  # xvfb by default and don't have to worry about the distinction, it
132*6777b538SAndroid Build Coastguard Worker  # can remain solely under the control of the test invocation itself.
133*6777b538SAndroid Build Coastguard Worker  use_xvfb = True
134*6777b538SAndroid Build Coastguard Worker  if '--no-xvfb' in cmd:
135*6777b538SAndroid Build Coastguard Worker    use_xvfb = False
136*6777b538SAndroid Build Coastguard Worker    cmd.remove('--no-xvfb')
137*6777b538SAndroid Build Coastguard Worker
138*6777b538SAndroid Build Coastguard Worker  # Xorg is mostly a drop in replacement to Xvfb but has better support for
139*6777b538SAndroid Build Coastguard Worker  # dummy drivers and multi-screen testing (See: crbug.com/40257169 and
140*6777b538SAndroid Build Coastguard Worker  # http://tinyurl.com/4phsuupf). Requires Xorg binaries
141*6777b538SAndroid Build Coastguard Worker  # (package: xserver-xorg-core)
142*6777b538SAndroid Build Coastguard Worker  use_xorg = False
143*6777b538SAndroid Build Coastguard Worker  if '--use-xorg' in cmd:
144*6777b538SAndroid Build Coastguard Worker    use_xvfb = False
145*6777b538SAndroid Build Coastguard Worker    use_xorg = True
146*6777b538SAndroid Build Coastguard Worker    cmd.remove('--use-xorg')
147*6777b538SAndroid Build Coastguard Worker
148*6777b538SAndroid Build Coastguard Worker  # Tests that run on Linux platforms with Ozone/Wayland backend require
149*6777b538SAndroid Build Coastguard Worker  # a Weston instance. However, it is also required to disable xvfb so
150*6777b538SAndroid Build Coastguard Worker  # that Weston can run in a pure headless environment.
151*6777b538SAndroid Build Coastguard Worker  use_weston = False
152*6777b538SAndroid Build Coastguard Worker  if '--use-weston' in cmd:
153*6777b538SAndroid Build Coastguard Worker    if use_xvfb or use_xorg:
154*6777b538SAndroid Build Coastguard Worker      print('Unable to use Weston with xvfb or Xorg.\n', file=sys.stderr)
155*6777b538SAndroid Build Coastguard Worker      return 1
156*6777b538SAndroid Build Coastguard Worker    use_weston = True
157*6777b538SAndroid Build Coastguard Worker    cmd.remove('--use-weston')
158*6777b538SAndroid Build Coastguard Worker
159*6777b538SAndroid Build Coastguard Worker  if sys.platform.startswith('linux') and (use_xvfb or use_xorg):
160*6777b538SAndroid Build Coastguard Worker    return _run_with_x11(cmd, env, stdoutfile, use_openbox, use_xcompmgr,
161*6777b538SAndroid Build Coastguard Worker      use_xorg, xvfb_whd or DEFAULT_XVFB_WHD, cwd)
162*6777b538SAndroid Build Coastguard Worker  if use_weston:
163*6777b538SAndroid Build Coastguard Worker    return _run_with_weston(cmd, env, stdoutfile, cwd)
164*6777b538SAndroid Build Coastguard Worker  return test_env.run_executable(cmd, env, stdoutfile, cwd)
165*6777b538SAndroid Build Coastguard Worker
166*6777b538SAndroid Build Coastguard Worker
167*6777b538SAndroid Build Coastguard Workerdef _make_xorg_config(whd):
168*6777b538SAndroid Build Coastguard Worker  """Generates an Xorg config file based on the specified WxHxD string and
169*6777b538SAndroid Build Coastguard Worker  returns the file path. See:
170*6777b538SAndroid Build Coastguard Worker  https://www.x.org/releases/current/doc/man/man5/xorg.conf.5.xhtml"""
171*6777b538SAndroid Build Coastguard Worker  (width, height, depth) = whd.split('x')
172*6777b538SAndroid Build Coastguard Worker  modeline = subprocess.check_output(
173*6777b538SAndroid Build Coastguard Worker    ['cvt', width, height, '60'], stderr=subprocess.STDOUT, text=True)
174*6777b538SAndroid Build Coastguard Worker  modeline_label = re.search(
175*6777b538SAndroid Build Coastguard Worker    'Modeline "(.*)"', modeline, re.IGNORECASE).group(1)
176*6777b538SAndroid Build Coastguard Worker  config = f"""
177*6777b538SAndroid Build Coastguard WorkerSection "Monitor"
178*6777b538SAndroid Build Coastguard Worker  Identifier "Monitor0"
179*6777b538SAndroid Build Coastguard Worker  {modeline}
180*6777b538SAndroid Build Coastguard WorkerEndSection
181*6777b538SAndroid Build Coastguard WorkerSection "Device"
182*6777b538SAndroid Build Coastguard Worker  Identifier "Device0"
183*6777b538SAndroid Build Coastguard Worker  # Dummy driver requires package `xserver-xorg-video-dummy`.
184*6777b538SAndroid Build Coastguard Worker  Driver "dummy"
185*6777b538SAndroid Build Coastguard Worker  VideoRam 256000
186*6777b538SAndroid Build Coastguard WorkerEndSection
187*6777b538SAndroid Build Coastguard WorkerSection "Screen"
188*6777b538SAndroid Build Coastguard Worker  DefaultDepth {depth}
189*6777b538SAndroid Build Coastguard Worker  Identifier "Screen0"
190*6777b538SAndroid Build Coastguard Worker  Device "Device0"
191*6777b538SAndroid Build Coastguard Worker  Monitor "Monitor0"
192*6777b538SAndroid Build Coastguard Worker  SubSection "Display"
193*6777b538SAndroid Build Coastguard Worker    Depth {depth}
194*6777b538SAndroid Build Coastguard Worker    Modes "{modeline_label}"
195*6777b538SAndroid Build Coastguard Worker  EndSubSection
196*6777b538SAndroid Build Coastguard WorkerEndSection
197*6777b538SAndroid Build Coastguard Worker  """
198*6777b538SAndroid Build Coastguard Worker  config_file = os.path.join(tempfile.gettempdir(), 'xorg.config')
199*6777b538SAndroid Build Coastguard Worker  with open(config_file, 'w') as f:
200*6777b538SAndroid Build Coastguard Worker    f.write(config)
201*6777b538SAndroid Build Coastguard Worker  return config_file
202*6777b538SAndroid Build Coastguard Worker
203*6777b538SAndroid Build Coastguard Workerdef _run_with_x11(cmd, env, stdoutfile, use_openbox,
204*6777b538SAndroid Build Coastguard Worker                   use_xcompmgr, use_xorg, xvfb_whd, cwd):
205*6777b538SAndroid Build Coastguard Worker  """Runs with an X11 server. Uses Xvfb by default and Xorg when use_xorg is
206*6777b538SAndroid Build Coastguard Worker  True."""
207*6777b538SAndroid Build Coastguard Worker  openbox_proc = None
208*6777b538SAndroid Build Coastguard Worker  openbox_ready = MutableBoolean()
209*6777b538SAndroid Build Coastguard Worker  def set_openbox_ready(*_):
210*6777b538SAndroid Build Coastguard Worker    openbox_ready.setvalue(True)
211*6777b538SAndroid Build Coastguard Worker
212*6777b538SAndroid Build Coastguard Worker  xcompmgr_proc = None
213*6777b538SAndroid Build Coastguard Worker  x11_proc = None
214*6777b538SAndroid Build Coastguard Worker  x11_ready = MutableBoolean()
215*6777b538SAndroid Build Coastguard Worker  def set_x11_ready(*_):
216*6777b538SAndroid Build Coastguard Worker    x11_ready.setvalue(True)
217*6777b538SAndroid Build Coastguard Worker
218*6777b538SAndroid Build Coastguard Worker  dbus_pid = None
219*6777b538SAndroid Build Coastguard Worker  x11_binary = 'Xorg' if use_xorg else 'Xvfb'
220*6777b538SAndroid Build Coastguard Worker  xorg_config_file = _make_xorg_config(xvfb_whd) if use_xorg else None
221*6777b538SAndroid Build Coastguard Worker  try:
222*6777b538SAndroid Build Coastguard Worker    signal.signal(signal.SIGTERM, raise_x11_error)
223*6777b538SAndroid Build Coastguard Worker    signal.signal(signal.SIGINT, raise_x11_error)
224*6777b538SAndroid Build Coastguard Worker
225*6777b538SAndroid Build Coastguard Worker    xvfb_help = None
226*6777b538SAndroid Build Coastguard Worker    if not use_xorg:
227*6777b538SAndroid Build Coastguard Worker      # Before [1], the maximum number of X11 clients was 256.  After, the
228*6777b538SAndroid Build Coastguard Worker      # default limit is 256 with a configurable maximum of 512.  On systems
229*6777b538SAndroid Build Coastguard Worker      # with a large number of CPUs, the old limit of 256 may be hit for certain
230*6777b538SAndroid Build Coastguard Worker      # test suites [2] [3], so we set the limit to 512 when possible.  This
231*6777b538SAndroid Build Coastguard Worker      # flag is not available on Ubuntu 16.04 or 18.04, so a feature check is
232*6777b538SAndroid Build Coastguard Worker      # required.  Xvfb does not have a '-version' option, so checking the
233*6777b538SAndroid Build Coastguard Worker      # '-help' output is required.
234*6777b538SAndroid Build Coastguard Worker      #
235*6777b538SAndroid Build Coastguard Worker      # [1] d206c240c0b85c4da44f073d6e9a692afb6b96d2
236*6777b538SAndroid Build Coastguard Worker      # [2] https://crbug.com/1187948
237*6777b538SAndroid Build Coastguard Worker      # [3] https://crbug.com/1120107
238*6777b538SAndroid Build Coastguard Worker      xvfb_help = subprocess.check_output(
239*6777b538SAndroid Build Coastguard Worker        ['Xvfb', '-help'], stderr=subprocess.STDOUT).decode('utf8')
240*6777b538SAndroid Build Coastguard Worker
241*6777b538SAndroid Build Coastguard Worker    # Due to race condition for display number, Xvfb/Xorg might fail to run.
242*6777b538SAndroid Build Coastguard Worker    # If it does fail, try again up to 10 times, similarly to xvfb-run.
243*6777b538SAndroid Build Coastguard Worker    for _ in range(10):
244*6777b538SAndroid Build Coastguard Worker      x11_ready.setvalue(False)
245*6777b538SAndroid Build Coastguard Worker      display = find_display()
246*6777b538SAndroid Build Coastguard Worker
247*6777b538SAndroid Build Coastguard Worker      x11_cmd = None
248*6777b538SAndroid Build Coastguard Worker      if use_xorg:
249*6777b538SAndroid Build Coastguard Worker        x11_cmd = ['Xorg', display, '-config', xorg_config_file]
250*6777b538SAndroid Build Coastguard Worker      else:
251*6777b538SAndroid Build Coastguard Worker        x11_cmd = ['Xvfb', display, '-screen', '0', xvfb_whd, '-ac',
252*6777b538SAndroid Build Coastguard Worker                    '-nolisten', 'tcp', '-dpi', '96', '+extension', 'RANDR']
253*6777b538SAndroid Build Coastguard Worker        if '-maxclients' in xvfb_help:
254*6777b538SAndroid Build Coastguard Worker          x11_cmd += ['-maxclients', '512']
255*6777b538SAndroid Build Coastguard Worker
256*6777b538SAndroid Build Coastguard Worker      # Sets SIGUSR1 to ignore for Xvfb/Xorg to signal current process
257*6777b538SAndroid Build Coastguard Worker      # when it is ready. Due to race condition, USR1 signal could be sent
258*6777b538SAndroid Build Coastguard Worker      # before the process resets the signal handler, we cannot rely on
259*6777b538SAndroid Build Coastguard Worker      # signal handler to change on time.
260*6777b538SAndroid Build Coastguard Worker      signal.signal(signal.SIGUSR1, signal.SIG_IGN)
261*6777b538SAndroid Build Coastguard Worker      x11_proc = subprocess.Popen(x11_cmd, stderr=subprocess.STDOUT, env=env)
262*6777b538SAndroid Build Coastguard Worker      signal.signal(signal.SIGUSR1, set_x11_ready)
263*6777b538SAndroid Build Coastguard Worker      for _ in range(30):
264*6777b538SAndroid Build Coastguard Worker        time.sleep(.1)  # gives Xvfb/Xorg time to start or fail.
265*6777b538SAndroid Build Coastguard Worker        if x11_ready.getvalue() or x11_proc.poll() is not None:
266*6777b538SAndroid Build Coastguard Worker          break  # xvfb/xorg sent ready signal, or already failed and stopped.
267*6777b538SAndroid Build Coastguard Worker
268*6777b538SAndroid Build Coastguard Worker      if x11_proc.poll() is None:
269*6777b538SAndroid Build Coastguard Worker        if x11_ready.getvalue():
270*6777b538SAndroid Build Coastguard Worker          break  # xvfb/xorg is ready
271*6777b538SAndroid Build Coastguard Worker        kill(x11_proc, x11_binary)  # still not ready, give up and retry
272*6777b538SAndroid Build Coastguard Worker
273*6777b538SAndroid Build Coastguard Worker    if x11_proc.poll() is not None:
274*6777b538SAndroid Build Coastguard Worker      raise _X11ProcessError('Failed to start after 10 tries')
275*6777b538SAndroid Build Coastguard Worker
276*6777b538SAndroid Build Coastguard Worker    env['DISPLAY'] = display
277*6777b538SAndroid Build Coastguard Worker    # Set dummy variable for scripts.
278*6777b538SAndroid Build Coastguard Worker    env['XVFB_DISPLAY'] = display
279*6777b538SAndroid Build Coastguard Worker
280*6777b538SAndroid Build Coastguard Worker    dbus_pid = launch_dbus(env)
281*6777b538SAndroid Build Coastguard Worker
282*6777b538SAndroid Build Coastguard Worker    if use_openbox:
283*6777b538SAndroid Build Coastguard Worker      # Openbox will send a SIGUSR1 signal to the current process notifying the
284*6777b538SAndroid Build Coastguard Worker      # script it has started up.
285*6777b538SAndroid Build Coastguard Worker      current_proc_id = os.getpid()
286*6777b538SAndroid Build Coastguard Worker
287*6777b538SAndroid Build Coastguard Worker      # The CMD that is passed via the --startup flag.
288*6777b538SAndroid Build Coastguard Worker      openbox_startup_cmd = 'kill --signal SIGUSR1 %s' % str(current_proc_id)
289*6777b538SAndroid Build Coastguard Worker      # Setup the signal handlers before starting the openbox instance.
290*6777b538SAndroid Build Coastguard Worker      signal.signal(signal.SIGUSR1, signal.SIG_IGN)
291*6777b538SAndroid Build Coastguard Worker      signal.signal(signal.SIGUSR1, set_openbox_ready)
292*6777b538SAndroid Build Coastguard Worker      openbox_proc = subprocess.Popen(
293*6777b538SAndroid Build Coastguard Worker          ['openbox', '--sm-disable', '--startup',
294*6777b538SAndroid Build Coastguard Worker           openbox_startup_cmd], stderr=subprocess.STDOUT, env=env)
295*6777b538SAndroid Build Coastguard Worker
296*6777b538SAndroid Build Coastguard Worker      for _ in range(30):
297*6777b538SAndroid Build Coastguard Worker        time.sleep(.1)  # gives Openbox time to start or fail.
298*6777b538SAndroid Build Coastguard Worker        if openbox_ready.getvalue() or openbox_proc.poll() is not None:
299*6777b538SAndroid Build Coastguard Worker          break  # openbox sent ready signal, or failed and stopped.
300*6777b538SAndroid Build Coastguard Worker
301*6777b538SAndroid Build Coastguard Worker      if openbox_proc.poll() is not None or not openbox_ready.getvalue():
302*6777b538SAndroid Build Coastguard Worker        raise _X11ProcessError('Failed to start OpenBox.')
303*6777b538SAndroid Build Coastguard Worker
304*6777b538SAndroid Build Coastguard Worker    if use_xcompmgr:
305*6777b538SAndroid Build Coastguard Worker      xcompmgr_proc = subprocess.Popen(
306*6777b538SAndroid Build Coastguard Worker          'xcompmgr', stderr=subprocess.STDOUT, env=env)
307*6777b538SAndroid Build Coastguard Worker
308*6777b538SAndroid Build Coastguard Worker    return test_env.run_executable(cmd, env, stdoutfile, cwd)
309*6777b538SAndroid Build Coastguard Worker  except OSError as e:
310*6777b538SAndroid Build Coastguard Worker    print('Failed to start %s or Openbox: %s\n' % (x11_binary, str(e)),
311*6777b538SAndroid Build Coastguard Worker          file=sys.stderr)
312*6777b538SAndroid Build Coastguard Worker    return 1
313*6777b538SAndroid Build Coastguard Worker  except _X11ProcessError as e:
314*6777b538SAndroid Build Coastguard Worker    print('%s fail: %s\n' % (x11_binary, str(e)), file=sys.stderr)
315*6777b538SAndroid Build Coastguard Worker    return 1
316*6777b538SAndroid Build Coastguard Worker  finally:
317*6777b538SAndroid Build Coastguard Worker    kill(openbox_proc, 'openbox')
318*6777b538SAndroid Build Coastguard Worker    kill(xcompmgr_proc, 'xcompmgr')
319*6777b538SAndroid Build Coastguard Worker    kill(x11_proc, x11_binary)
320*6777b538SAndroid Build Coastguard Worker    if xorg_config_file != None:
321*6777b538SAndroid Build Coastguard Worker      os.remove(xorg_config_file)
322*6777b538SAndroid Build Coastguard Worker
323*6777b538SAndroid Build Coastguard Worker    # dbus-daemon is not a subprocess, so we can't SIGTERM+waitpid() on it.
324*6777b538SAndroid Build Coastguard Worker    # To ensure it exits, use SIGKILL which should be safe since all other
325*6777b538SAndroid Build Coastguard Worker    # processes that it would have been servicing have exited.
326*6777b538SAndroid Build Coastguard Worker    if dbus_pid:
327*6777b538SAndroid Build Coastguard Worker      os.kill(dbus_pid, signal.SIGKILL)
328*6777b538SAndroid Build Coastguard Worker
329*6777b538SAndroid Build Coastguard Worker
330*6777b538SAndroid Build Coastguard Worker# TODO(https://crbug.com/1060466): Write tests.
331*6777b538SAndroid Build Coastguard Workerdef _run_with_weston(cmd, env, stdoutfile, cwd):
332*6777b538SAndroid Build Coastguard Worker  weston_proc = None
333*6777b538SAndroid Build Coastguard Worker
334*6777b538SAndroid Build Coastguard Worker  try:
335*6777b538SAndroid Build Coastguard Worker    signal.signal(signal.SIGTERM, raise_weston_error)
336*6777b538SAndroid Build Coastguard Worker    signal.signal(signal.SIGINT, raise_weston_error)
337*6777b538SAndroid Build Coastguard Worker
338*6777b538SAndroid Build Coastguard Worker    dbus_pid = launch_dbus(env)
339*6777b538SAndroid Build Coastguard Worker
340*6777b538SAndroid Build Coastguard Worker    # The bundled weston (//third_party/weston) is used by Linux Ozone Wayland
341*6777b538SAndroid Build Coastguard Worker    # CI and CQ testers and compiled by //ui/ozone/platform/wayland whenever
342*6777b538SAndroid Build Coastguard Worker    # there is a dependency on the Ozone/Wayland and use_bundled_weston is set
343*6777b538SAndroid Build Coastguard Worker    # in gn args. However, some tests do not require Wayland or do not use
344*6777b538SAndroid Build Coastguard Worker    # //ui/ozone at all, but still have --use-weston flag set by the
345*6777b538SAndroid Build Coastguard Worker    # OZONE_WAYLAND variant (see //testing/buildbot/variants.pyl). This results
346*6777b538SAndroid Build Coastguard Worker    # in failures and those tests cannot be run because of the exception that
347*6777b538SAndroid Build Coastguard Worker    # informs about missing weston binary. Thus, to overcome the issue before
348*6777b538SAndroid Build Coastguard Worker    # a better solution is found, add a check for the "weston" binary here and
349*6777b538SAndroid Build Coastguard Worker    # run tests without Wayland compositor if the weston binary is not found.
350*6777b538SAndroid Build Coastguard Worker    # TODO(https://1178788): find a better solution.
351*6777b538SAndroid Build Coastguard Worker    if not os.path.isfile("./weston"):
352*6777b538SAndroid Build Coastguard Worker      print('Weston is not available. Starting without Wayland compositor')
353*6777b538SAndroid Build Coastguard Worker      return test_env.run_executable(cmd, env, stdoutfile, cwd)
354*6777b538SAndroid Build Coastguard Worker
355*6777b538SAndroid Build Coastguard Worker    # Set $XDG_RUNTIME_DIR if it is not set.
356*6777b538SAndroid Build Coastguard Worker    _set_xdg_runtime_dir(env)
357*6777b538SAndroid Build Coastguard Worker
358*6777b538SAndroid Build Coastguard Worker    # Write options that can't be passed via CLI flags to the config file.
359*6777b538SAndroid Build Coastguard Worker    # 1) panel-position=none - disables the panel, which might interfere with
360*6777b538SAndroid Build Coastguard Worker    # the tests by blocking mouse input.
361*6777b538SAndroid Build Coastguard Worker    with open(_weston_config_file_path(), 'w') as weston_config_file:
362*6777b538SAndroid Build Coastguard Worker      weston_config_file.write('[shell]\npanel-position=none')
363*6777b538SAndroid Build Coastguard Worker
364*6777b538SAndroid Build Coastguard Worker    # Weston is compiled along with the Ozone/Wayland platform, and is
365*6777b538SAndroid Build Coastguard Worker    # fetched as data deps. Thus, run it from the current directory.
366*6777b538SAndroid Build Coastguard Worker    #
367*6777b538SAndroid Build Coastguard Worker    # Weston is used with the following flags:
368*6777b538SAndroid Build Coastguard Worker    # 1) --backend=headless-backend.so - runs Weston in a headless mode
369*6777b538SAndroid Build Coastguard Worker    # that does not require a real GPU card.
370*6777b538SAndroid Build Coastguard Worker    # 2) --idle-time=0 - disables idle timeout, which prevents Weston
371*6777b538SAndroid Build Coastguard Worker    # to enter idle state. Otherwise, Weston stops to send frame callbacks,
372*6777b538SAndroid Build Coastguard Worker    # and tests start to time out (this typically happens after 300 seconds -
373*6777b538SAndroid Build Coastguard Worker    # the default time after which Weston enters the idle state).
374*6777b538SAndroid Build Coastguard Worker    # 3) --modules=ui-controls.so,systemd-notify.so - enables support for the
375*6777b538SAndroid Build Coastguard Worker    # ui-controls Wayland protocol extension and the systemd-notify protocol.
376*6777b538SAndroid Build Coastguard Worker    # 4) --width && --height set size of a virtual display: we need to set
377*6777b538SAndroid Build Coastguard Worker    # an adequate size so that tests can have more room for managing size
378*6777b538SAndroid Build Coastguard Worker    # of windows.
379*6777b538SAndroid Build Coastguard Worker    # 5) --config=... - tells Weston to use our custom config.
380*6777b538SAndroid Build Coastguard Worker    weston_cmd = ['./weston', '--backend=headless-backend.so', '--idle-time=0',
381*6777b538SAndroid Build Coastguard Worker          '--modules=ui-controls.so,systemd-notify.so', '--width=1280',
382*6777b538SAndroid Build Coastguard Worker          '--height=800', '--config=' + _weston_config_file_path()]
383*6777b538SAndroid Build Coastguard Worker
384*6777b538SAndroid Build Coastguard Worker    if '--weston-use-gl' in cmd:
385*6777b538SAndroid Build Coastguard Worker      # Runs Weston using hardware acceleration instead of SwiftShader.
386*6777b538SAndroid Build Coastguard Worker      weston_cmd.append('--use-gl')
387*6777b538SAndroid Build Coastguard Worker      cmd.remove('--weston-use-gl')
388*6777b538SAndroid Build Coastguard Worker
389*6777b538SAndroid Build Coastguard Worker    if '--weston-debug-logging' in cmd:
390*6777b538SAndroid Build Coastguard Worker      cmd.remove('--weston-debug-logging')
391*6777b538SAndroid Build Coastguard Worker      env = copy.deepcopy(env)
392*6777b538SAndroid Build Coastguard Worker      env['WAYLAND_DEBUG'] = '1'
393*6777b538SAndroid Build Coastguard Worker
394*6777b538SAndroid Build Coastguard Worker    # We use the systemd-notify protocol to detect whether weston has launched
395*6777b538SAndroid Build Coastguard Worker    # successfully. We listen on a unix socket and set the NOTIFY_SOCKET
396*6777b538SAndroid Build Coastguard Worker    # environment variable to the socket's path. If we tell it to load its
397*6777b538SAndroid Build Coastguard Worker    # systemd-notify module, weston will send a 'READY=1' message to the socket
398*6777b538SAndroid Build Coastguard Worker    # once it has loaded that module.
399*6777b538SAndroid Build Coastguard Worker    # See the sd_notify(3) man page and weston's compositor/systemd-notify.c for
400*6777b538SAndroid Build Coastguard Worker    # more details.
401*6777b538SAndroid Build Coastguard Worker    with socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM
402*6777b538SAndroid Build Coastguard Worker                       | socket.SOCK_NONBLOCK) as notify_socket:
403*6777b538SAndroid Build Coastguard Worker      notify_socket.bind(_weston_notify_socket_address())
404*6777b538SAndroid Build Coastguard Worker      env['NOTIFY_SOCKET'] = _weston_notify_socket_address()
405*6777b538SAndroid Build Coastguard Worker
406*6777b538SAndroid Build Coastguard Worker      weston_proc_display = None
407*6777b538SAndroid Build Coastguard Worker      for _ in range(10):
408*6777b538SAndroid Build Coastguard Worker        weston_proc = subprocess.Popen(
409*6777b538SAndroid Build Coastguard Worker          weston_cmd,
410*6777b538SAndroid Build Coastguard Worker          stderr=subprocess.STDOUT, env=env)
411*6777b538SAndroid Build Coastguard Worker
412*6777b538SAndroid Build Coastguard Worker        for _ in range(25):
413*6777b538SAndroid Build Coastguard Worker          time.sleep(0.1)  # Gives weston some time to start.
414*6777b538SAndroid Build Coastguard Worker          try:
415*6777b538SAndroid Build Coastguard Worker            if notify_socket.recv(512) == b'READY=1':
416*6777b538SAndroid Build Coastguard Worker              break
417*6777b538SAndroid Build Coastguard Worker          except BlockingIOError:
418*6777b538SAndroid Build Coastguard Worker            continue
419*6777b538SAndroid Build Coastguard Worker
420*6777b538SAndroid Build Coastguard Worker        for _ in range(25):
421*6777b538SAndroid Build Coastguard Worker          # The 'READY=1' message is sent as soon as weston loads the
422*6777b538SAndroid Build Coastguard Worker          # systemd-notify module. This happens shortly before spawning its
423*6777b538SAndroid Build Coastguard Worker          # subprocesses (e.g. desktop-shell). Wait some more to ensure they
424*6777b538SAndroid Build Coastguard Worker          # have been spawned.
425*6777b538SAndroid Build Coastguard Worker          time.sleep(0.1)
426*6777b538SAndroid Build Coastguard Worker
427*6777b538SAndroid Build Coastguard Worker          # Get the $WAYLAND_DISPLAY set by Weston and pass it to the test
428*6777b538SAndroid Build Coastguard Worker          # launcher. Please note that this env variable is local for the
429*6777b538SAndroid Build Coastguard Worker          # process. That's the reason we have to read it from Weston
430*6777b538SAndroid Build Coastguard Worker          # separately.
431*6777b538SAndroid Build Coastguard Worker          weston_proc_display = _get_display_from_weston(weston_proc.pid)
432*6777b538SAndroid Build Coastguard Worker          if weston_proc_display is not None:
433*6777b538SAndroid Build Coastguard Worker            break # Weston could launch and we found the display.
434*6777b538SAndroid Build Coastguard Worker
435*6777b538SAndroid Build Coastguard Worker        # Also break from the outer loop.
436*6777b538SAndroid Build Coastguard Worker        if weston_proc_display is not None:
437*6777b538SAndroid Build Coastguard Worker          break
438*6777b538SAndroid Build Coastguard Worker
439*6777b538SAndroid Build Coastguard Worker    # If we couldn't find the display after 10 tries, raise an exception.
440*6777b538SAndroid Build Coastguard Worker    if weston_proc_display is None:
441*6777b538SAndroid Build Coastguard Worker      raise _WestonProcessError('Failed to start Weston.')
442*6777b538SAndroid Build Coastguard Worker
443*6777b538SAndroid Build Coastguard Worker    env.pop('NOTIFY_SOCKET')
444*6777b538SAndroid Build Coastguard Worker
445*6777b538SAndroid Build Coastguard Worker    env['WAYLAND_DISPLAY'] = weston_proc_display
446*6777b538SAndroid Build Coastguard Worker    if '--chrome-wayland-debugging' in cmd:
447*6777b538SAndroid Build Coastguard Worker      cmd.remove('--chrome-wayland-debugging')
448*6777b538SAndroid Build Coastguard Worker      env['WAYLAND_DEBUG'] = '1'
449*6777b538SAndroid Build Coastguard Worker    else:
450*6777b538SAndroid Build Coastguard Worker      env['WAYLAND_DEBUG'] = '0'
451*6777b538SAndroid Build Coastguard Worker
452*6777b538SAndroid Build Coastguard Worker    return test_env.run_executable(cmd, env, stdoutfile, cwd)
453*6777b538SAndroid Build Coastguard Worker  except OSError as e:
454*6777b538SAndroid Build Coastguard Worker    print('Failed to start Weston: %s\n' % str(e), file=sys.stderr)
455*6777b538SAndroid Build Coastguard Worker    return 1
456*6777b538SAndroid Build Coastguard Worker  except _WestonProcessError as e:
457*6777b538SAndroid Build Coastguard Worker    print('Weston fail: %s\n' % str(e), file=sys.stderr)
458*6777b538SAndroid Build Coastguard Worker    return 1
459*6777b538SAndroid Build Coastguard Worker  finally:
460*6777b538SAndroid Build Coastguard Worker    kill(weston_proc, 'weston')
461*6777b538SAndroid Build Coastguard Worker
462*6777b538SAndroid Build Coastguard Worker    if os.path.exists(_weston_notify_socket_address()):
463*6777b538SAndroid Build Coastguard Worker      os.remove(_weston_notify_socket_address())
464*6777b538SAndroid Build Coastguard Worker
465*6777b538SAndroid Build Coastguard Worker    if os.path.exists(_weston_config_file_path()):
466*6777b538SAndroid Build Coastguard Worker      os.remove(_weston_config_file_path())
467*6777b538SAndroid Build Coastguard Worker
468*6777b538SAndroid Build Coastguard Worker    # dbus-daemon is not a subprocess, so we can't SIGTERM+waitpid() on it.
469*6777b538SAndroid Build Coastguard Worker    # To ensure it exits, use SIGKILL which should be safe since all other
470*6777b538SAndroid Build Coastguard Worker    # processes that it would have been servicing have exited.
471*6777b538SAndroid Build Coastguard Worker    if dbus_pid:
472*6777b538SAndroid Build Coastguard Worker      os.kill(dbus_pid, signal.SIGKILL)
473*6777b538SAndroid Build Coastguard Worker
474*6777b538SAndroid Build Coastguard Workerdef _weston_notify_socket_address():
475*6777b538SAndroid Build Coastguard Worker  return os.path.join(tempfile.gettempdir(), '.xvfb.py-weston-notify.sock')
476*6777b538SAndroid Build Coastguard Worker
477*6777b538SAndroid Build Coastguard Workerdef _weston_config_file_path():
478*6777b538SAndroid Build Coastguard Worker  return os.path.join(tempfile.gettempdir(), '.xvfb.py-weston.ini')
479*6777b538SAndroid Build Coastguard Worker
480*6777b538SAndroid Build Coastguard Workerdef _get_display_from_weston(weston_proc_pid):
481*6777b538SAndroid Build Coastguard Worker  """Retrieves $WAYLAND_DISPLAY set by Weston.
482*6777b538SAndroid Build Coastguard Worker
483*6777b538SAndroid Build Coastguard Worker  Returns the $WAYLAND_DISPLAY variable from one of weston's subprocesses.
484*6777b538SAndroid Build Coastguard Worker
485*6777b538SAndroid Build Coastguard Worker  Weston updates this variable early in its startup in the main process, but we
486*6777b538SAndroid Build Coastguard Worker  can only read the environment variables as they were when the process was
487*6777b538SAndroid Build Coastguard Worker  created. Therefore we must use one of weston's subprocesses, which are all
488*6777b538SAndroid Build Coastguard Worker  spawned with the new value for $WAYLAND_DISPLAY. Any of them will do, as they
489*6777b538SAndroid Build Coastguard Worker  all have the same value set.
490*6777b538SAndroid Build Coastguard Worker
491*6777b538SAndroid Build Coastguard Worker  Args:
492*6777b538SAndroid Build Coastguard Worker    weston_proc_pid: The process of id of the main Weston process.
493*6777b538SAndroid Build Coastguard Worker
494*6777b538SAndroid Build Coastguard Worker  Returns:
495*6777b538SAndroid Build Coastguard Worker    the display set by Wayland, which clients can use to connect to.
496*6777b538SAndroid Build Coastguard Worker  """
497*6777b538SAndroid Build Coastguard Worker
498*6777b538SAndroid Build Coastguard Worker  # Take the parent process.
499*6777b538SAndroid Build Coastguard Worker  parent = psutil.Process(weston_proc_pid)
500*6777b538SAndroid Build Coastguard Worker  if parent is None:
501*6777b538SAndroid Build Coastguard Worker    return None # The process is not found. Give up.
502*6777b538SAndroid Build Coastguard Worker
503*6777b538SAndroid Build Coastguard Worker  # Traverse through all the children processes and find one that has
504*6777b538SAndroid Build Coastguard Worker  # $WAYLAND_DISPLAY set.
505*6777b538SAndroid Build Coastguard Worker  children = parent.children(recursive=True)
506*6777b538SAndroid Build Coastguard Worker  for process in children:
507*6777b538SAndroid Build Coastguard Worker    weston_proc_display = process.environ().get('WAYLAND_DISPLAY')
508*6777b538SAndroid Build Coastguard Worker    # If display is set, Weston could start successfully and we can use
509*6777b538SAndroid Build Coastguard Worker    # that display for Wayland connection in Chromium.
510*6777b538SAndroid Build Coastguard Worker    if weston_proc_display is not None:
511*6777b538SAndroid Build Coastguard Worker      return weston_proc_display
512*6777b538SAndroid Build Coastguard Worker  return None
513*6777b538SAndroid Build Coastguard Worker
514*6777b538SAndroid Build Coastguard Worker
515*6777b538SAndroid Build Coastguard Workerclass MutableBoolean(object):
516*6777b538SAndroid Build Coastguard Worker  """Simple mutable boolean class. Used to be mutated inside an handler."""
517*6777b538SAndroid Build Coastguard Worker
518*6777b538SAndroid Build Coastguard Worker  def __init__(self):
519*6777b538SAndroid Build Coastguard Worker    self._val = False
520*6777b538SAndroid Build Coastguard Worker
521*6777b538SAndroid Build Coastguard Worker  def setvalue(self, val):
522*6777b538SAndroid Build Coastguard Worker    assert isinstance(val, bool)
523*6777b538SAndroid Build Coastguard Worker    self._val = val
524*6777b538SAndroid Build Coastguard Worker
525*6777b538SAndroid Build Coastguard Worker  def getvalue(self):
526*6777b538SAndroid Build Coastguard Worker    return self._val
527*6777b538SAndroid Build Coastguard Worker
528*6777b538SAndroid Build Coastguard Worker
529*6777b538SAndroid Build Coastguard Workerdef raise_x11_error(*_):
530*6777b538SAndroid Build Coastguard Worker  raise _X11ProcessError('Terminated')
531*6777b538SAndroid Build Coastguard Worker
532*6777b538SAndroid Build Coastguard Worker
533*6777b538SAndroid Build Coastguard Workerdef raise_weston_error(*_):
534*6777b538SAndroid Build Coastguard Worker  raise _WestonProcessError('Terminated')
535*6777b538SAndroid Build Coastguard Worker
536*6777b538SAndroid Build Coastguard Worker
537*6777b538SAndroid Build Coastguard Workerdef find_display():
538*6777b538SAndroid Build Coastguard Worker  """Iterates through X-lock files to find an available display number.
539*6777b538SAndroid Build Coastguard Worker
540*6777b538SAndroid Build Coastguard Worker  The lower bound follows xvfb-run standard at 99, and the upper bound
541*6777b538SAndroid Build Coastguard Worker  is set to 119.
542*6777b538SAndroid Build Coastguard Worker
543*6777b538SAndroid Build Coastguard Worker  Returns:
544*6777b538SAndroid Build Coastguard Worker    A string of a random available display number for Xvfb ':{99-119}'.
545*6777b538SAndroid Build Coastguard Worker
546*6777b538SAndroid Build Coastguard Worker  Raises:
547*6777b538SAndroid Build Coastguard Worker    _X11ProcessError: Raised when displays 99 through 119 are unavailable.
548*6777b538SAndroid Build Coastguard Worker  """
549*6777b538SAndroid Build Coastguard Worker
550*6777b538SAndroid Build Coastguard Worker  available_displays = [
551*6777b538SAndroid Build Coastguard Worker      d for d in range(99, 120)
552*6777b538SAndroid Build Coastguard Worker      if not os.path.isfile('/tmp/.X{}-lock'.format(d))
553*6777b538SAndroid Build Coastguard Worker  ]
554*6777b538SAndroid Build Coastguard Worker  if available_displays:
555*6777b538SAndroid Build Coastguard Worker    return ':{}'.format(random.choice(available_displays))
556*6777b538SAndroid Build Coastguard Worker  raise _X11ProcessError('Failed to find display number')
557*6777b538SAndroid Build Coastguard Worker
558*6777b538SAndroid Build Coastguard Worker
559*6777b538SAndroid Build Coastguard Workerdef _set_xdg_runtime_dir(env):
560*6777b538SAndroid Build Coastguard Worker  """Sets the $XDG_RUNTIME_DIR variable if it hasn't been set before."""
561*6777b538SAndroid Build Coastguard Worker  runtime_dir = env.get('XDG_RUNTIME_DIR')
562*6777b538SAndroid Build Coastguard Worker  if not runtime_dir:
563*6777b538SAndroid Build Coastguard Worker    runtime_dir = '/tmp/xdg-tmp-dir/'
564*6777b538SAndroid Build Coastguard Worker    if not os.path.exists(runtime_dir):
565*6777b538SAndroid Build Coastguard Worker      os.makedirs(runtime_dir, 0o700)
566*6777b538SAndroid Build Coastguard Worker    env['XDG_RUNTIME_DIR'] = runtime_dir
567*6777b538SAndroid Build Coastguard Worker
568*6777b538SAndroid Build Coastguard Worker
569*6777b538SAndroid Build Coastguard Workerdef main():
570*6777b538SAndroid Build Coastguard Worker  usage = ('Usage: xvfb.py '
571*6777b538SAndroid Build Coastguard Worker           '[command [--no-xvfb or --use_xorg or --use-weston] args...]')
572*6777b538SAndroid Build Coastguard Worker  # TODO(crbug.com/326283384): Argparse-ify this.
573*6777b538SAndroid Build Coastguard Worker  if len(sys.argv) < 2:
574*6777b538SAndroid Build Coastguard Worker    print(usage + '\n', file=sys.stderr)
575*6777b538SAndroid Build Coastguard Worker    return 2
576*6777b538SAndroid Build Coastguard Worker
577*6777b538SAndroid Build Coastguard Worker  # If the user still thinks the first argument is the execution directory then
578*6777b538SAndroid Build Coastguard Worker  # print a friendly error message and quit.
579*6777b538SAndroid Build Coastguard Worker  if os.path.isdir(sys.argv[1]):
580*6777b538SAndroid Build Coastguard Worker    print('Invalid command: \"%s\" is a directory\n' % sys.argv[1],
581*6777b538SAndroid Build Coastguard Worker          file=sys.stderr)
582*6777b538SAndroid Build Coastguard Worker    print(usage + '\n', file=sys.stderr)
583*6777b538SAndroid Build Coastguard Worker    return 3
584*6777b538SAndroid Build Coastguard Worker
585*6777b538SAndroid Build Coastguard Worker  return run_executable(sys.argv[1:], os.environ.copy())
586*6777b538SAndroid Build Coastguard Worker
587*6777b538SAndroid Build Coastguard Worker
588*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__':
589*6777b538SAndroid Build Coastguard Worker  sys.exit(main())
590