# 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. """Functions to setup xvfb, which is used by the linux machines. """ import os import signal import subprocess import tempfile import time def xvfb_display_index(_child_build_name): return '9' def xvfb_pid_filename(child_build_name): """Returns the filename to the Xvfb pid file. This name is unique for each builder. This is used by the linux builders.""" return os.path.join( tempfile.gettempdir(), 'xvfb-' + xvfb_display_index(child_build_name) + '.pid' ) def start_virtual_x(child_build_name, build_dir): """Start a virtual X server and set the DISPLAY environment variable so sub processes will use the virtual X server. Also start openbox. This only works on Linux and assumes that xvfb and openbox are installed. Args: child_build_name: The name of the build that we use for the pid file. E.g., webkit-rel-linux. build_dir: The directory where binaries are produced. If this is non-empty, we try running xdisplaycheck from |build_dir| to verify our X connection. """ # We use a pid file to make sure we don't have any xvfb processes running # from a previous test run. stop_virtual_x(child_build_name) xdisplaycheck_path = None if build_dir: xdisplaycheck_path = os.path.join(build_dir, 'xdisplaycheck') display = ':%s' % xvfb_display_index(child_build_name) # Note we don't add the optional screen here (+ '.0') os.environ['DISPLAY'] = display # Parts of Xvfb use a hard-coded "/tmp" for its temporary directory. # This can cause a failure when those parts expect to hardlink against # files that were created in "TEMPDIR" / "TMPDIR". # # See: https://crbug.com/715848 env = os.environ.copy() if env.get('TMPDIR') and env['TMPDIR'] != '/tmp': print('Overriding TMPDIR to "/tmp" for Xvfb, was: %s' % (env['TMPDIR'],)) env['TMPDIR'] = '/tmp' if xdisplaycheck_path and os.path.exists(xdisplaycheck_path): print('Verifying Xvfb is not running ...') checkstarttime = time.time() xdisplayproc = subprocess.Popen([xdisplaycheck_path, '--noserver'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env) # Wait for xdisplaycheck to exit. logs = xdisplayproc.communicate()[0] if xdisplayproc.returncode == 0: print('xdisplaycheck says there is a display still running, exiting...') raise Exception('Display already present.') xvfb_lock_filename = '/tmp/.X%s-lock' % xvfb_display_index(child_build_name) if os.path.exists(xvfb_lock_filename): print('Removing stale xvfb lock file %r' % xvfb_lock_filename) try: os.unlink(xvfb_lock_filename) except OSError as err: print('Removing xvfb lock file failed: %s' % err) # Figure out which X server to try. cmd = 'Xvfb' # Start a virtual X server that we run the tests in. This makes it so we can # run the tests even if we didn't start the tests from an X session. proc = subprocess.Popen([ cmd, display, '-screen', '0', '1280x800x24', '-ac', '-dpi', '96', '-maxclients', '512', '-extension', 'MIT-SHM' ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env) pid_filename = xvfb_pid_filename(child_build_name) open(pid_filename, 'w').write(str(proc.pid)) # Wait for Xvfb to start up. time.sleep(10) # Verify that Xvfb has started by using xdisplaycheck. if xdisplaycheck_path and os.path.exists(xdisplaycheck_path): print('Verifying Xvfb has started...') checkstarttime = time.time() xdisplayproc = subprocess.Popen([xdisplaycheck_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # Wait for xdisplaycheck to exit. logs = xdisplayproc.communicate()[0] checktime = time.time() - checkstarttime if xdisplayproc.returncode != 0: print('xdisplaycheck failed after %d seconds.' % checktime) print('xdisplaycheck output:') for line in logs.splitlines(): print('> %s' % line) return_code = proc.poll() if return_code is None: print('Xvfb still running, stopping.') proc.terminate() else: print('Xvfb exited, code %d' % return_code) print('Xvfb output:') for line in proc.communicate()[0].splitlines(): print('> %s' % line) raise Exception(logs) print('xdisplaycheck succeeded after %d seconds.' % checktime) print('xdisplaycheck output:') for line in logs.splitlines(): print('> %s' % line) print('...OK') # Some ChromeOS tests need a window manager. subprocess.Popen('openbox', stdout=subprocess.PIPE, stderr=subprocess.STDOUT) print('Window manager (openbox) started.') def stop_virtual_x(child_build_name): """Try and stop the virtual X server if one was started with StartVirtualX. When the X server dies, it takes down the window manager with it. If a virtual x server is not running, this method does nothing.""" pid_filename = xvfb_pid_filename(child_build_name) if os.path.exists(pid_filename): xvfb_pid = int(open(pid_filename).read()) print('Stopping Xvfb with pid %d ...' % xvfb_pid) # If the process doesn't exist, we raise an exception that we can ignore. try: os.kill(xvfb_pid, signal.SIGKILL) except OSError: print('... killing failed, presuming unnecessary.') os.remove(pid_filename) print('Xvfb pid file removed')