summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/mozrunner/mozrunner/cli.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mozbase/mozrunner/mozrunner/cli.py')
-rw-r--r--testing/mozbase/mozrunner/mozrunner/cli.py181
1 files changed, 181 insertions, 0 deletions
diff --git a/testing/mozbase/mozrunner/mozrunner/cli.py b/testing/mozbase/mozrunner/mozrunner/cli.py
new file mode 100644
index 0000000000..941734de5e
--- /dev/null
+++ b/testing/mozbase/mozrunner/mozrunner/cli.py
@@ -0,0 +1,181 @@
+# 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/.
+
+import os
+import sys
+
+from mozprofile import MozProfileCLI
+
+from .application import get_app_context
+from .runners import runners
+from .utils import findInPath
+
+# 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
+
+ :param debugger: name or path to debugger
+ :param arguments: arguments for the debugger, or None to use defaults
+ :param interactive: whether the debugger should run in interactive mode
+
+ """
+ # 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 CLI(MozProfileCLI):
+ """Command line interface"""
+
+ module = "mozrunner"
+
+ def __init__(self, args=sys.argv[1:]):
+ MozProfileCLI.__init__(self, args=args)
+
+ # choose appropriate runner and profile classes
+ app = self.options.app
+ try:
+ self.runner_class = runners[app]
+ self.profile_class = get_app_context(app).profile_class
+ except KeyError:
+ self.parser.error(
+ 'Application "%s" unknown (should be one of "%s")'
+ % (app, ", ".join(runners.keys()))
+ )
+
+ def add_options(self, parser):
+ """add options to the parser"""
+ parser.description = (
+ "Reliable start/stop/configuration of Mozilla"
+ " Applications (Firefox, Thunderbird, etc.)"
+ )
+
+ # 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="store",
+ help="arguments to the debugger",
+ )
+ parser.add_option(
+ "--interactive",
+ dest="interactive",
+ action="store_true",
+ help="run the program interactively",
+ )
+
+ # methods for running
+
+ def command_args(self):
+ """additional arguments for the mozilla application"""
+ # pylint --py3k: W1636
+ return list(map(os.path.expanduser, self.options.appArgs))
+
+ def runner_args(self):
+ """arguments to instantiate the runner class"""
+ return dict(cmdargs=self.command_args(), binary=self.options.binary)
+
+ def create_runner(self):
+ profile = self.profile_class(**self.profile_args())
+ return self.runner_class(profile=profile, **self.runner_args())
+
+ def run(self):
+ runner = self.create_runner()
+ self.start(runner)
+ runner.cleanup()
+
+ def debugger_arguments(self):
+ """Get the debugger arguments
+
+ returns a 2-tuple of debugger arguments:
+ (debugger_arguments, interactive)
+
+ """
+ debug_args = self.options.debugger_args
+ if debug_args is not None:
+ debug_args = debug_args.split()
+ interactive = self.options.interactive
+ if self.options.debugger:
+ debug_args, interactive = debugger_arguments(
+ self.options.debugger, debug_args, interactive
+ )
+ return debug_args, interactive
+
+ def start(self, runner):
+ """Starts the runner and waits for the application to exit
+
+ It can also happen via a keyboard interrupt. It should 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()