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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/mozrunner/mozrunner/__init__.py ('k') | third_party/mozrunner/mozrunner/utils.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
Added: svn:executable
+ *
OLDNEW
(Empty)
1 #!/usr/bin/env python
2
3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 # You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 __all__ = ['Runner', 'ThunderbirdRunner', 'FirefoxRunner', 'runners', 'CLI', 'cl i', 'package_metadata']
8
9 import mozinfo
10 import optparse
11 import os
12 import platform
13 import subprocess
14 import sys
15 import ConfigParser
16
17 from utils import get_metadata_from_egg
18 from utils import findInPath
19 from mozprofile import *
20 from mozprocess.processhandler import ProcessHandler
21
22 if mozinfo.isMac:
23 from plistlib import readPlist
24
25 package_metadata = get_metadata_from_egg('mozrunner')
26
27 # Map of debugging programs to information about them
28 # from http://mxr.mozilla.org/mozilla-central/source/build/automationutils.py#59
29 debuggers = {'gdb': {'interactive': True,
30 'args': ['-q', '--args'],},
31 'valgrind': {'interactive': False,
32 'args': ['--leak-check=full']}
33 }
34
35 def debugger_arguments(debugger, arguments=None, interactive=None):
36 """
37 finds debugger arguments from debugger given and defaults
38 * debugger : debugger name or path to debugger
39 * arguments : arguments to the debugger, or None to use defaults
40 * interactive : whether the debugger should be run in interactive mode, or N one to use default
41 """
42
43 # find debugger executable if not a file
44 executable = debugger
45 if not os.path.exists(executable):
46 executable = findInPath(debugger)
47 if executable is None:
48 raise Exception("Path to '%s' not found" % debugger)
49
50 # if debugger not in dictionary of knowns return defaults
51 dirname, debugger = os.path.split(debugger)
52 if debugger not in debuggers:
53 return ([executable] + (arguments or []), bool(interactive))
54
55 # otherwise use the dictionary values for arguments unless specified
56 if arguments is None:
57 arguments = debuggers[debugger].get('args', [])
58 if interactive is None:
59 interactive = debuggers[debugger].get('interactive', False)
60 return ([executable] + arguments, interactive)
61
62 class Runner(object):
63 """Handles all running operations. Finds bins, runs and kills the process."" "
64
65 profile_class = Profile # profile class to use by default
66
67 @classmethod
68 def create(cls, binary=None, cmdargs=None, env=None, kp_kwargs=None, profile _args=None,
69 clean_profile=True, process_class=ProcessHandler):
70 profile = cls.profile_class(**(profile_args or {}))
71 return cls(profile, binary=binary, cmdargs=cmdargs, env=env, kp_kwargs=k p_kwargs,
72 clean_profile=clean_profile, process_ class=process_class)
73
74 def __init__(self, profile, binary, cmdargs=None, env=None,
75 kp_kwargs=None, clean_profile=True, process_class=ProcessHandle r):
76 self.process_handler = None
77 self.process_class = process_class
78 self.profile = profile
79 self.clean_profile = clean_profile
80
81 # find the binary
82 self.binary = binary
83 if not self.binary:
84 raise Exception("Binary not specified")
85 if not os.path.exists(self.binary):
86 raise OSError("Binary path does not exist: %s" % self.binary)
87
88 # allow Mac binaries to be specified as an app bundle
89 plist = '%s/Contents/Info.plist' % self.binary
90 if mozinfo.isMac and os.path.exists(plist):
91 info = readPlist(plist)
92 self.binary = os.path.join(self.binary, "Contents/MacOS/",
93 info['CFBundleExecutable'])
94
95 self.cmdargs = cmdargs or []
96 _cmdargs = [i for i in self.cmdargs
97 if i != '-foreground']
98 if len(_cmdargs) != len(self.cmdargs):
99 # foreground should be last; see
100 # - https://bugzilla.mozilla.org/show_bug.cgi?id=625614
101 # - https://bugzilla.mozilla.org/show_bug.cgi?id=626826
102 self.cmdargs = _cmdargs
103 self.cmdargs.append('-foreground')
104
105 # process environment
106 if env is None:
107 self.env = os.environ.copy()
108 else:
109 self.env = env.copy()
110 # allows you to run an instance of Firefox separately from any other ins tances
111 self.env['MOZ_NO_REMOTE'] = '1'
112 # keeps Firefox attached to the terminal window after it starts
113 self.env['NO_EM_RESTART'] = '1'
114
115 # set the library path if needed on linux
116 if sys.platform == 'linux2' and self.binary.endswith('-bin'):
117 dirname = os.path.dirname(self.binary)
118 if os.environ.get('LD_LIBRARY_PATH', None):
119 self.env['LD_LIBRARY_PATH'] = '%s:%s' % (os.environ['LD_LIBRARY_ PATH'], dirname)
120 else:
121 self.env['LD_LIBRARY_PATH'] = dirname
122
123 # arguments for ProfessHandler.Process
124 self.kp_kwargs = kp_kwargs or {}
125
126 @property
127 def command(self):
128 """Returns the command list to run."""
129 return [self.binary, '-profile', self.profile.profile]
130
131 def get_repositoryInfo(self):
132 """Read repository information from application.ini and platform.ini."""
133
134 config = ConfigParser.RawConfigParser()
135 dirname = os.path.dirname(self.binary)
136 repository = { }
137
138 for file, section in [('application', 'App'), ('platform', 'Build')]:
139 config.read(os.path.join(dirname, '%s.ini' % file))
140
141 for key, id in [('SourceRepository', 'repository'),
142 ('SourceStamp', 'changeset')]:
143 try:
144 repository['%s_%s' % (file, id)] = config.get(section, key);
145 except:
146 repository['%s_%s' % (file, id)] = None
147
148 return repository
149
150 def is_running(self):
151 return self.process_handler is not None
152
153 def start(self, debug_args=None, interactive=False, timeout=None, outputTime out=None):
154 """
155 Run self.command in the proper environment.
156 - debug_args: arguments for the debugger
157 - interactive: uses subprocess.Popen directly
158 - read_output: sends program output to stdout [default=False]
159 - timeout: see process_handler.waitForFinish
160 - outputTimeout: see process_handler.waitForFinish
161 """
162
163 # ensure you are stopped
164 self.stop()
165
166 # ensure the profile exists
167 if not self.profile.exists():
168 self.profile.reset()
169 assert self.profile.exists(), "%s : failure to reset profile" % self .__class__.__name__
170
171 cmd = self._wrap_command(self.command+self.cmdargs)
172
173 # attach a debugger, if specified
174 if debug_args:
175 cmd = list(debug_args) + cmd
176
177 if interactive:
178 self.process_handler = subprocess.Popen(cmd, env=self.env)
179 # TODO: other arguments
180 else:
181 # this run uses the managed processhandler
182 self.process_handler = self.process_class(cmd, env=self.env, **self. kp_kwargs)
183 self.process_handler.run(timeout, outputTimeout)
184
185 def wait(self, timeout=None):
186 """
187 Wait for the app to exit.
188
189 If timeout is not None, will return after timeout seconds.
190 Use is_running() to determine whether or not a timeout occured.
191 Timeout is ignored if interactive was set to True.
192 """
193 if self.process_handler is None:
194 return
195
196 if isinstance(self.process_handler, subprocess.Popen):
197 self.process_handler.wait()
198 else:
199 self.process_handler.wait(timeout)
200 if self.process_handler.proc.poll() is None:
201 # waitForFinish timed out
202 return
203
204 self.process_handler = None
205
206 def stop(self):
207 """Kill the app"""
208 if self.process_handler is None:
209 return
210 self.process_handler.kill()
211 self.process_handler = None
212
213 def reset(self):
214 """
215 reset the runner between runs
216 currently, only resets the profile, but probably should do more
217 """
218 self.profile.reset()
219
220 def cleanup(self):
221 self.stop()
222 if self.clean_profile:
223 self.profile.cleanup()
224
225 def _wrap_command(self, cmd):
226 """
227 If running on OS X 10.5 or older, wrap |cmd| so that it will
228 be executed as an i386 binary, in case it's a 32-bit/64-bit universal
229 binary.
230 """
231 if mozinfo.isMac and hasattr(platform, 'mac_ver') and \
232 platform.mac_ver()[0][:4] < '10.6':
233 return ["arch", "-arch", "i386"] + cmd
234 return cmd
235
236 __del__ = cleanup
237
238
239 class FirefoxRunner(Runner):
240 """Specialized Runner subclass for running Firefox."""
241
242 profile_class = FirefoxProfile
243
244 def __init__(self, profile, binary=None, **kwargs):
245
246 # take the binary from BROWSER_PATH environment variable
247 if (not binary) and 'BROWSER_PATH' in os.environ:
248 binary = os.environ['BROWSER_PATH']
249
250 Runner.__init__(self, profile, binary, **kwargs)
251
252 class ThunderbirdRunner(Runner):
253 """Specialized Runner subclass for running Thunderbird"""
254 profile_class = ThunderbirdProfile
255
256 runners = {'firefox': FirefoxRunner,
257 'thunderbird': ThunderbirdRunner}
258
259 class CLI(MozProfileCLI):
260 """Command line interface."""
261
262 module = "mozrunner"
263
264 def __init__(self, args=sys.argv[1:]):
265 """
266 Setup command line parser and parse arguments
267 - args : command line arguments
268 """
269
270 self.metadata = getattr(sys.modules[self.module],
271 'package_metadata',
272 {})
273 version = self.metadata.get('Version')
274 parser_args = {'description': self.metadata.get('Summary')}
275 if version:
276 parser_args['version'] = "%prog " + version
277 self.parser = optparse.OptionParser(**parser_args)
278 self.add_options(self.parser)
279 (self.options, self.args) = self.parser.parse_args(args)
280
281 if getattr(self.options, 'info', None):
282 self.print_metadata()
283 sys.exit(0)
284
285 # choose appropriate runner and profile classes
286 try:
287 self.runner_class = runners[self.options.app]
288 except KeyError:
289 self.parser.error('Application "%s" unknown (should be one of "firef ox" or "thunderbird")' % self.options.app)
290
291 def add_options(self, parser):
292 """add options to the parser"""
293
294 # add profile options
295 MozProfileCLI.add_options(self, parser)
296
297 # add runner options
298 parser.add_option('-b', "--binary",
299 dest="binary", help="Binary path.",
300 metavar=None, default=None)
301 parser.add_option('--app', dest='app', default='firefox',
302 help="Application to use [DEFAULT: %default]")
303 parser.add_option('--app-arg', dest='appArgs',
304 default=[], action='append',
305 help="provides an argument to the test application")
306 parser.add_option('--debugger', dest='debugger',
307 help="run under a debugger, e.g. gdb or valgrind")
308 parser.add_option('--debugger-args', dest='debugger_args',
309 action='append', default=None,
310 help="arguments to the debugger")
311 parser.add_option('--interactive', dest='interactive',
312 action='store_true',
313 help="run the program interactively")
314 if self.metadata:
315 parser.add_option("--info", dest="info", default=False,
316 action="store_true",
317 help="Print module information")
318
319 ### methods for introspecting data
320
321 def get_metadata_from_egg(self):
322 import pkg_resources
323 ret = {}
324 dist = pkg_resources.get_distribution(self.module)
325 if dist.has_metadata("PKG-INFO"):
326 for line in dist.get_metadata_lines("PKG-INFO"):
327 key, value = line.split(':', 1)
328 ret[key] = value
329 if dist.has_metadata("requires.txt"):
330 ret["Dependencies"] = "\n" + dist.get_metadata("requires.txt")
331 return ret
332
333 def print_metadata(self, data=("Name", "Version", "Summary", "Home-page",
334 "Author", "Author-email", "License", "Platfor m", "Dependencies")):
335 for key in data:
336 if key in self.metadata:
337 print key + ": " + self.metadata[key]
338
339 ### methods for running
340
341 def command_args(self):
342 """additional arguments for the mozilla application"""
343 return self.options.appArgs
344
345 def runner_args(self):
346 """arguments to instantiate the runner class"""
347 return dict(cmdargs=self.command_args(),
348 binary=self.options.binary,
349 profile_args=self.profile_args())
350
351 def create_runner(self):
352 return self.runner_class.create(**self.runner_args())
353
354 def run(self):
355 runner = self.create_runner()
356 self.start(runner)
357 runner.cleanup()
358
359 def debugger_arguments(self):
360 """
361 returns a 2-tuple of debugger arguments:
362 (debugger_arguments, interactive)
363 """
364 debug_args = self.options.debugger_args
365 interactive = self.options.interactive
366 if self.options.debugger:
367 debug_args, interactive = debugger_arguments(self.options.debugger)
368 return debug_args, interactive
369
370 def start(self, runner):
371 """Starts the runner and waits for Firefox to exit or Keyboard Interrupt .
372 Shoule be overwritten to provide custom running of the runner instance." ""
373
374 # attach a debugger if specified
375 debug_args, interactive = self.debugger_arguments()
376 runner.start(debug_args=debug_args, interactive=interactive)
377 print 'Starting:', ' '.join(runner.command)
378 try:
379 runner.wait()
380 except KeyboardInterrupt:
381 runner.stop()
382
383
384 def cli(args=sys.argv[1:]):
385 CLI(args).run()
386
387 if __name__ == '__main__':
388 cli()
OLDNEW
« 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