1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
# 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"""
return 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()
|