| # 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 platform |
| import signal |
| import subprocess |
| import tempfile |
| import time |
| |
| def _XvfbDisplayIndex(_slave_build_name): |
| return '9' |
| |
| def _XvfbPidFilename(slave_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-' + _XvfbDisplayIndex(slave_build_name) + '.pid') |
| |
| |
| def StartVirtualX(slave_build_name, build_dir, with_wm=True, server_dir=None): |
| """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: |
| slave_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. |
| with_wm: Whether we add a window manager to the display too. |
| server_dir: Directory to search for the server executable. |
| """ |
| # We use a pid file to make sure we don't have any xvfb processes running |
| # from a previous test run. |
| StopVirtualX(slave_build_name) |
| |
| xdisplaycheck_path = None |
| if build_dir: |
| xdisplaycheck_path = os.path.join(build_dir, 'xdisplaycheck') |
| |
| display = ':%s' % _XvfbDisplayIndex(slave_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' % _XvfbDisplayIndex(slave_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 e: |
| print 'Removing xvfb lock file failed: %s' % e |
| |
| # Figure out which X server to try. |
| cmd = 'Xvfb' |
| if server_dir and os.path.exists(server_dir): |
| cmd = os.path.join(server_dir, 'Xvfb.' + platform.architecture()[0]) |
| if not os.path.exists(cmd): |
| cmd = os.path.join(server_dir, 'Xvfb') |
| if not os.path.exists(cmd): |
| print 'No Xvfb found in designated server path:', server_dir |
| raise Exception('No virtual server') |
| |
| # 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'], |
| stdout=subprocess.PIPE, stderr=subprocess.STDOUT, |
| env=env) |
| xvfb_pid_filename = _XvfbPidFilename(slave_build_name) |
| open(xvfb_pid_filename, 'w').write(str(proc.pid)) |
| |
| # 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 l in logs.splitlines(): |
| print '> %s' % l |
| rc = proc.poll() |
| if rc is None: |
| print 'Xvfb still running, stopping.' |
| proc.terminate() |
| else: |
| print 'Xvfb exited, code %d' % rc |
| |
| print 'Xvfb output:' |
| for l in proc.communicate()[0].splitlines(): |
| print '> %s' % l |
| raise Exception(logs) |
| else: |
| print 'xdisplaycheck succeeded after %d seconds.' % checktime |
| print 'xdisplaycheck output:' |
| for l in logs.splitlines(): |
| print '> %s' % l |
| print '...OK' |
| |
| if with_wm: |
| # Some ChromeOS tests need a window manager. |
| subprocess.Popen('openbox', stdout=subprocess.PIPE, |
| stderr=subprocess.STDOUT) |
| print 'Window manager (openbox) started.' |
| else: |
| print 'No window manager required.' |
| |
| |
| |
| def StopVirtualX(slave_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.""" |
| xvfb_pid_filename = _XvfbPidFilename(slave_build_name) |
| if os.path.exists(xvfb_pid_filename): |
| xvfb_pid = int(open(xvfb_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(xvfb_pid_filename) |
| print 'Xvfb pid file removed' |