Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Unified Diff: third_party/mozrunner/mozrunner/runner.py

Issue 108313011: Adding mozilla libraries required by Firefox interop test. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/deps/
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/mozrunner/mozrunner/__init__.py ('k') | third_party/mozrunner/mozrunner/utils.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/mozrunner/mozrunner/runner.py
===================================================================
--- third_party/mozrunner/mozrunner/runner.py (revision 0)
+++ third_party/mozrunner/mozrunner/runner.py (revision 0)
@@ -0,0 +1,388 @@
+#!/usr/bin/env python
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+__all__ = ['Runner', 'ThunderbirdRunner', 'FirefoxRunner', 'runners', 'CLI', 'cli', 'package_metadata']
+
+import mozinfo
+import optparse
+import os
+import platform
+import subprocess
+import sys
+import ConfigParser
+
+from utils import get_metadata_from_egg
+from utils import findInPath
+from mozprofile import *
+from mozprocess.processhandler import ProcessHandler
+
+if mozinfo.isMac:
+ from plistlib import readPlist
+
+package_metadata = get_metadata_from_egg('mozrunner')
+
+# Map of debugging programs to information about them
+# from http://mxr.mozilla.org/mozilla-central/source/build/automationutils.py#59
+debuggers = {'gdb': {'interactive': True,
+ 'args': ['-q', '--args'],},
+ 'valgrind': {'interactive': False,
+ 'args': ['--leak-check=full']}
+ }
+
+def debugger_arguments(debugger, arguments=None, interactive=None):
+ """
+ finds debugger arguments from debugger given and defaults
+ * debugger : debugger name or path to debugger
+ * arguments : arguments to the debugger, or None to use defaults
+ * interactive : whether the debugger should be run in interactive mode, or None to use default
+ """
+
+ # find debugger executable if not a file
+ executable = debugger
+ if not os.path.exists(executable):
+ executable = findInPath(debugger)
+ if executable is None:
+ raise Exception("Path to '%s' not found" % debugger)
+
+ # if debugger not in dictionary of knowns return defaults
+ dirname, debugger = os.path.split(debugger)
+ if debugger not in debuggers:
+ return ([executable] + (arguments or []), bool(interactive))
+
+ # otherwise use the dictionary values for arguments unless specified
+ if arguments is None:
+ arguments = debuggers[debugger].get('args', [])
+ if interactive is None:
+ interactive = debuggers[debugger].get('interactive', False)
+ return ([executable] + arguments, interactive)
+
+class Runner(object):
+ """Handles all running operations. Finds bins, runs and kills the process."""
+
+ profile_class = Profile # profile class to use by default
+
+ @classmethod
+ def create(cls, binary=None, cmdargs=None, env=None, kp_kwargs=None, profile_args=None,
+ clean_profile=True, process_class=ProcessHandler):
+ profile = cls.profile_class(**(profile_args or {}))
+ return cls(profile, binary=binary, cmdargs=cmdargs, env=env, kp_kwargs=kp_kwargs,
+ clean_profile=clean_profile, process_class=process_class)
+
+ def __init__(self, profile, binary, cmdargs=None, env=None,
+ kp_kwargs=None, clean_profile=True, process_class=ProcessHandler):
+ self.process_handler = None
+ self.process_class = process_class
+ self.profile = profile
+ self.clean_profile = clean_profile
+
+ # find the binary
+ self.binary = binary
+ if not self.binary:
+ raise Exception("Binary not specified")
+ if not os.path.exists(self.binary):
+ raise OSError("Binary path does not exist: %s" % self.binary)
+
+ # allow Mac binaries to be specified as an app bundle
+ plist = '%s/Contents/Info.plist' % self.binary
+ if mozinfo.isMac and os.path.exists(plist):
+ info = readPlist(plist)
+ self.binary = os.path.join(self.binary, "Contents/MacOS/",
+ info['CFBundleExecutable'])
+
+ self.cmdargs = cmdargs or []
+ _cmdargs = [i for i in self.cmdargs
+ if i != '-foreground']
+ if len(_cmdargs) != len(self.cmdargs):
+ # foreground should be last; see
+ # - https://bugzilla.mozilla.org/show_bug.cgi?id=625614
+ # - https://bugzilla.mozilla.org/show_bug.cgi?id=626826
+ self.cmdargs = _cmdargs
+ self.cmdargs.append('-foreground')
+
+ # process environment
+ if env is None:
+ self.env = os.environ.copy()
+ else:
+ self.env = env.copy()
+ # allows you to run an instance of Firefox separately from any other instances
+ self.env['MOZ_NO_REMOTE'] = '1'
+ # keeps Firefox attached to the terminal window after it starts
+ self.env['NO_EM_RESTART'] = '1'
+
+ # set the library path if needed on linux
+ if sys.platform == 'linux2' and self.binary.endswith('-bin'):
+ dirname = os.path.dirname(self.binary)
+ if os.environ.get('LD_LIBRARY_PATH', None):
+ self.env['LD_LIBRARY_PATH'] = '%s:%s' % (os.environ['LD_LIBRARY_PATH'], dirname)
+ else:
+ self.env['LD_LIBRARY_PATH'] = dirname
+
+ # arguments for ProfessHandler.Process
+ self.kp_kwargs = kp_kwargs or {}
+
+ @property
+ def command(self):
+ """Returns the command list to run."""
+ return [self.binary, '-profile', self.profile.profile]
+
+ def get_repositoryInfo(self):
+ """Read repository information from application.ini and platform.ini."""
+
+ config = ConfigParser.RawConfigParser()
+ dirname = os.path.dirname(self.binary)
+ repository = { }
+
+ for file, section in [('application', 'App'), ('platform', 'Build')]:
+ config.read(os.path.join(dirname, '%s.ini' % file))
+
+ for key, id in [('SourceRepository', 'repository'),
+ ('SourceStamp', 'changeset')]:
+ try:
+ repository['%s_%s' % (file, id)] = config.get(section, key);
+ except:
+ repository['%s_%s' % (file, id)] = None
+
+ return repository
+
+ def is_running(self):
+ return self.process_handler is not None
+
+ def start(self, debug_args=None, interactive=False, timeout=None, outputTimeout=None):
+ """
+ Run self.command in the proper environment.
+ - debug_args: arguments for the debugger
+ - interactive: uses subprocess.Popen directly
+ - read_output: sends program output to stdout [default=False]
+ - timeout: see process_handler.waitForFinish
+ - outputTimeout: see process_handler.waitForFinish
+ """
+
+ # ensure you are stopped
+ self.stop()
+
+ # ensure the profile exists
+ if not self.profile.exists():
+ self.profile.reset()
+ assert self.profile.exists(), "%s : failure to reset profile" % self.__class__.__name__
+
+ cmd = self._wrap_command(self.command+self.cmdargs)
+
+ # attach a debugger, if specified
+ if debug_args:
+ cmd = list(debug_args) + cmd
+
+ if interactive:
+ self.process_handler = subprocess.Popen(cmd, env=self.env)
+ # TODO: other arguments
+ else:
+ # this run uses the managed processhandler
+ self.process_handler = self.process_class(cmd, env=self.env, **self.kp_kwargs)
+ self.process_handler.run(timeout, outputTimeout)
+
+ def wait(self, timeout=None):
+ """
+ Wait for the app to exit.
+
+ If timeout is not None, will return after timeout seconds.
+ Use is_running() to determine whether or not a timeout occured.
+ Timeout is ignored if interactive was set to True.
+ """
+ if self.process_handler is None:
+ return
+
+ if isinstance(self.process_handler, subprocess.Popen):
+ self.process_handler.wait()
+ else:
+ self.process_handler.wait(timeout)
+ if self.process_handler.proc.poll() is None:
+ # waitForFinish timed out
+ return
+
+ self.process_handler = None
+
+ def stop(self):
+ """Kill the app"""
+ if self.process_handler is None:
+ return
+ self.process_handler.kill()
+ self.process_handler = None
+
+ def reset(self):
+ """
+ reset the runner between runs
+ currently, only resets the profile, but probably should do more
+ """
+ self.profile.reset()
+
+ def cleanup(self):
+ self.stop()
+ if self.clean_profile:
+ self.profile.cleanup()
+
+ def _wrap_command(self, cmd):
+ """
+ If running on OS X 10.5 or older, wrap |cmd| so that it will
+ be executed as an i386 binary, in case it's a 32-bit/64-bit universal
+ binary.
+ """
+ if mozinfo.isMac and hasattr(platform, 'mac_ver') and \
+ platform.mac_ver()[0][:4] < '10.6':
+ return ["arch", "-arch", "i386"] + cmd
+ return cmd
+
+ __del__ = cleanup
+
+
+class FirefoxRunner(Runner):
+ """Specialized Runner subclass for running Firefox."""
+
+ profile_class = FirefoxProfile
+
+ def __init__(self, profile, binary=None, **kwargs):
+
+ # take the binary from BROWSER_PATH environment variable
+ if (not binary) and 'BROWSER_PATH' in os.environ:
+ binary = os.environ['BROWSER_PATH']
+
+ Runner.__init__(self, profile, binary, **kwargs)
+
+class ThunderbirdRunner(Runner):
+ """Specialized Runner subclass for running Thunderbird"""
+ profile_class = ThunderbirdProfile
+
+runners = {'firefox': FirefoxRunner,
+ 'thunderbird': ThunderbirdRunner}
+
+class CLI(MozProfileCLI):
+ """Command line interface."""
+
+ module = "mozrunner"
+
+ def __init__(self, args=sys.argv[1:]):
+ """
+ Setup command line parser and parse arguments
+ - args : command line arguments
+ """
+
+ self.metadata = getattr(sys.modules[self.module],
+ 'package_metadata',
+ {})
+ version = self.metadata.get('Version')
+ parser_args = {'description': self.metadata.get('Summary')}
+ if version:
+ parser_args['version'] = "%prog " + version
+ self.parser = optparse.OptionParser(**parser_args)
+ self.add_options(self.parser)
+ (self.options, self.args) = self.parser.parse_args(args)
+
+ if getattr(self.options, 'info', None):
+ self.print_metadata()
+ sys.exit(0)
+
+ # choose appropriate runner and profile classes
+ try:
+ self.runner_class = runners[self.options.app]
+ except KeyError:
+ self.parser.error('Application "%s" unknown (should be one of "firefox" or "thunderbird")' % self.options.app)
+
+ def add_options(self, parser):
+ """add options to the parser"""
+
+ # add profile options
+ MozProfileCLI.add_options(self, parser)
+
+ # add runner options
+ parser.add_option('-b', "--binary",
+ dest="binary", help="Binary path.",
+ metavar=None, default=None)
+ parser.add_option('--app', dest='app', default='firefox',
+ help="Application to use [DEFAULT: %default]")
+ parser.add_option('--app-arg', dest='appArgs',
+ default=[], action='append',
+ help="provides an argument to the test application")
+ parser.add_option('--debugger', dest='debugger',
+ help="run under a debugger, e.g. gdb or valgrind")
+ parser.add_option('--debugger-args', dest='debugger_args',
+ action='append', default=None,
+ help="arguments to the debugger")
+ parser.add_option('--interactive', dest='interactive',
+ action='store_true',
+ help="run the program interactively")
+ if self.metadata:
+ parser.add_option("--info", dest="info", default=False,
+ action="store_true",
+ help="Print module information")
+
+ ### methods for introspecting data
+
+ def get_metadata_from_egg(self):
+ import pkg_resources
+ ret = {}
+ dist = pkg_resources.get_distribution(self.module)
+ if dist.has_metadata("PKG-INFO"):
+ for line in dist.get_metadata_lines("PKG-INFO"):
+ key, value = line.split(':', 1)
+ ret[key] = value
+ if dist.has_metadata("requires.txt"):
+ ret["Dependencies"] = "\n" + dist.get_metadata("requires.txt")
+ return ret
+
+ def print_metadata(self, data=("Name", "Version", "Summary", "Home-page",
+ "Author", "Author-email", "License", "Platform", "Dependencies")):
+ for key in data:
+ if key in self.metadata:
+ print key + ": " + self.metadata[key]
+
+ ### methods for running
+
+ def command_args(self):
+ """additional arguments for the mozilla application"""
+ return self.options.appArgs
+
+ def runner_args(self):
+ """arguments to instantiate the runner class"""
+ return dict(cmdargs=self.command_args(),
+ binary=self.options.binary,
+ profile_args=self.profile_args())
+
+ def create_runner(self):
+ return self.runner_class.create(**self.runner_args())
+
+ def run(self):
+ runner = self.create_runner()
+ self.start(runner)
+ runner.cleanup()
+
+ def debugger_arguments(self):
+ """
+ returns a 2-tuple of debugger arguments:
+ (debugger_arguments, interactive)
+ """
+ debug_args = self.options.debugger_args
+ interactive = self.options.interactive
+ if self.options.debugger:
+ debug_args, interactive = debugger_arguments(self.options.debugger)
+ return debug_args, interactive
+
+ def start(self, runner):
+ """Starts the runner and waits for Firefox to exit or Keyboard Interrupt.
+ Shoule be overwritten to provide custom running of the runner instance."""
+
+ # attach a debugger if specified
+ debug_args, interactive = self.debugger_arguments()
+ runner.start(debug_args=debug_args, interactive=interactive)
+ print 'Starting:', ' '.join(runner.command)
+ try:
+ runner.wait()
+ except KeyboardInterrupt:
+ runner.stop()
+
+
+def cli(args=sys.argv[1:]):
+ CLI(args).run()
+
+if __name__ == '__main__':
+ cli()
Property changes on: third_party/mozrunner/mozrunner/runner.py
___________________________________________________________________
Added: svn:eol-style
+ LF
Added: svn:executable
+ *
« no previous file with comments | « third_party/mozrunner/mozrunner/__init__.py ('k') | third_party/mozrunner/mozrunner/utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698