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

Unified Diff: ppapi/native_client/tools/browser_tester/browser_tester.py

Issue 10914053: Relocating files in the nacl repo that belong in chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: merge Created 8 years, 3 months 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
Index: ppapi/native_client/tools/browser_tester/browser_tester.py
diff --git a/ppapi/native_client/tools/browser_tester/browser_tester.py b/ppapi/native_client/tools/browser_tester/browser_tester.py
new file mode 100755
index 0000000000000000000000000000000000000000..1617210f4d66000796af7bb00b3c29ba863b8089
--- /dev/null
+++ b/ppapi/native_client/tools/browser_tester/browser_tester.py
@@ -0,0 +1,351 @@
+#!/usr/bin/python
+# 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.
+
+import glob
+import optparse
+import os.path
+import socket
+import sys
+import thread
+import time
+import urllib
+
+# Allow the import of third party modules
+script_dir = os.path.dirname(os.path.abspath(__file__))
+sys.path.append(os.path.join(script_dir, '../../../../third_party/'))
+sys.path.append(os.path.join(script_dir, '../../../../tools/valgrind/'))
+
+import browsertester.browserlauncher
+import browsertester.rpclistener
+import browsertester.server
+
+import memcheck_analyze
+import tsan_analyze
+
+def BuildArgParser():
+ usage = 'usage: %prog [options]'
+ parser = optparse.OptionParser(usage)
+
+ parser.add_option('-p', '--port', dest='port', action='store', type='int',
+ default='0', help='The TCP port the server will bind to. '
+ 'The default is to pick an unused port number.')
+ parser.add_option('--browser_path', dest='browser_path', action='store',
+ type='string', default=None,
+ help='Use the browser located here.')
+ parser.add_option('--map_file', dest='map_files', action='append',
+ type='string', nargs=2, default=[],
+ metavar='DEST SRC',
+ help='Add file SRC to be served from the HTTP server, '
+ 'to be made visible under the path DEST.')
+ parser.add_option('--serving_dir', dest='serving_dirs', action='append',
+ type='string', default=[],
+ metavar='DIRNAME',
+ help='Add directory DIRNAME to be served from the HTTP '
+ 'server to be made visible under the root.')
+ parser.add_option('--test_arg', dest='test_args', action='append',
+ type='string', nargs=2, default=[],
+ metavar='KEY VALUE',
+ help='Parameterize the test with a key/value pair.')
+ parser.add_option('--redirect_url', dest='map_redirects', action='append',
+ type='string', nargs=2, default=[],
+ metavar='DEST SRC',
+ help='Add a redirect to the HTTP server, '
+ 'requests for SRC will result in a redirect (302) to DEST.')
+ parser.add_option('--prefer_portable_in_manifest',
+ dest='prefer_portable_in_manifest',
+ action='store_true', default=False,
+ help='Use portable programs in manifest if available.')
+ parser.add_option('-f', '--file', dest='files', action='append',
+ type='string', default=[],
+ metavar='FILENAME',
+ help='Add a file to serve from the HTTP server, to be '
+ 'made visible in the root directory. '
+ '"--file path/to/foo.html" is equivalent to '
+ '"--map_file foo.html path/to/foo.html"')
+ parser.add_option('--mime_type', dest='mime_types', action='append',
+ type='string', nargs=2, default=[], metavar='DEST SRC',
+ help='Map file extension SRC to MIME type DEST when '
+ 'serving it from the HTTP server.')
+ parser.add_option('-u', '--url', dest='url', action='store',
+ type='string', default=None,
+ help='The webpage to load.')
+ parser.add_option('--ppapi_plugin', dest='ppapi_plugin', action='store',
+ type='string', default=None,
+ help='Use the browser plugin located here.')
+ parser.add_option('--sel_ldr', dest='sel_ldr', action='store',
+ type='string', default=None,
+ help='Use the sel_ldr located here.')
+ parser.add_option('--sel_ldr_bootstrap', dest='sel_ldr_bootstrap',
+ action='store', type='string', default=None,
+ help='Use the bootstrap loader located here.')
+ parser.add_option('--irt_library', dest='irt_library', action='store',
+ type='string', default=None,
+ help='Use the integrated runtime (IRT) library '
+ 'located here.')
+ parser.add_option('--interactive', dest='interactive', action='store_true',
+ default=False, help='Do not quit after testing is done. '
+ 'Handy for iterative development. Disables timeout.')
+ parser.add_option('--debug', dest='debug', action='store_true', default=False,
+ help='Request debugging output from browser.')
+ parser.add_option('--timeout', dest='timeout', action='store', type='float',
+ default=5.0,
+ help='The maximum amount of time to wait, in seconds, for '
+ 'the browser to make a request. The timer resets with each '
+ 'request.')
+ parser.add_option('--hard_timeout', dest='hard_timeout', action='store',
+ type='float', default=None,
+ help='The maximum amount of time to wait, in seconds, for '
+ 'the entire test. This will kill runaway tests. ')
+ parser.add_option('--allow_404', dest='allow_404', action='store_true',
+ default=False,
+ help='Allow 404s to occur without failing the test.')
+ parser.add_option('-b', '--bandwidth', dest='bandwidth', action='store',
+ type='float', default='0.0',
+ help='The amount of bandwidth (megabits / second) to '
+ 'simulate between the client and the server. This used for '
+ 'replies with file payloads. All other responses are '
+ 'assumed to be short. Bandwidth values <= 0.0 are assumed '
+ 'to mean infinite bandwidth.')
+ parser.add_option('--extension', dest='browser_extensions', action='append',
+ type='string', default=[],
+ help='Load the browser extensions located at the list of '
+ 'paths. Note: this currently only works with the Chrome '
+ 'browser.')
+ parser.add_option('--tool', dest='tool', action='store',
+ type='string', default=None,
+ help='Run tests under a tool.')
+ parser.add_option('--browser_flag', dest='browser_flags', action='append',
+ type='string', default=[],
+ help='Additional flags for the chrome command.')
+ parser.add_option('--enable_ppapi_dev', dest='enable_ppapi_dev',
+ action='store', type='int', default=1,
+ help='Enable/disable PPAPI Dev interfaces while testing.')
+ parser.add_option('--nacl_exe_stdin', dest='nacl_exe_stdin',
+ type='string', default=None,
+ help='Redirect standard input of NaCl executable.')
+ parser.add_option('--nacl_exe_stdout', dest='nacl_exe_stdout',
+ type='string', default=None,
+ help='Redirect standard output of NaCl executable.')
+ parser.add_option('--nacl_exe_stderr', dest='nacl_exe_stderr',
+ type='string', default=None,
+ help='Redirect standard error of NaCl executable.')
+
+ return parser
+
+
+def ProcessToolLogs(options, logs_dir):
+ if options.tool == 'memcheck':
+ analyzer = memcheck_analyze.MemcheckAnalyzer('', use_gdb=True)
+ logs_wildcard = 'xml.*'
+ elif options.tool == 'tsan':
+ analyzer = tsan_analyze.TsanAnalyzer('', use_gdb=True)
+ logs_wildcard = 'log.*'
+ files = glob.glob(os.path.join(logs_dir, logs_wildcard))
+ retcode = analyzer.Report(files, options.url)
+ return retcode
+
+
+# An exception that indicates possible flake.
+class RetryTest(Exception):
+ pass
+
+
+def DumpNetLog(netlog):
+ sys.stdout.write('\n')
+ if not os.path.isfile(netlog):
+ sys.stdout.write('Cannot find netlog, did Chrome actually launch?\n')
+ else:
+ sys.stdout.write('Netlog exists (%d bytes).\n' % os.path.getsize(netlog))
+ sys.stdout.write('Dumping it to stdout.\n\n\n')
+ sys.stdout.write(open(netlog).read())
+ sys.stdout.write('\n\n\n')
+
+
+# Try to discover the real IP address of this machine. If we can't figure it
+# out, fall back to localhost.
+# A windows bug makes using the loopback interface flaky in rare cases.
+# http://code.google.com/p/chromium/issues/detail?id=114369
+def GetHostName():
+ host = 'localhost'
+ try:
+ host = socket.gethostbyname(socket.gethostname())
+ except Exception:
+ pass
+ if host == '0.0.0.0':
+ host = 'localhost'
+ return host
+
+
+def RunTestsOnce(url, options):
+ # Set the default here so we're assured hard_timeout will be defined.
+ # Tests, such as run_inbrowser_trusted_crash_in_startup_test, may not use the
+ # RunFromCommand line entry point - and otherwise get stuck in an infinite
+ # loop when something goes wrong and the hard timeout is not set.
+ # http://code.google.com/p/chromium/issues/detail?id=105406
+ if options.hard_timeout is None:
+ options.hard_timeout = options.timeout * 4
+
+ options.files.append(os.path.join(script_dir, 'browserdata', 'nacltest.js'))
+
+ # Create server
+ host = GetHostName()
+ try:
+ server = browsertester.server.Create(host, options.port)
+ except Exception:
+ sys.stdout.write('Could not bind %r, falling back to localhost.\n' % host)
+ server = browsertester.server.Create('localhost', options.port)
+
+ # If port 0 has been requested, an arbitrary port will be bound so we need to
+ # query it. Older version of Python do not set server_address correctly when
+ # The requested port is 0 so we need to break encapsulation and query the
+ # socket directly.
+ host, port = server.socket.getsockname()
+
+ file_mapping = dict(options.map_files)
+ for filename in options.files:
+ file_mapping[os.path.basename(filename)] = filename
+ for server_path, real_path in file_mapping.iteritems():
+ if not os.path.exists(real_path):
+ raise AssertionError('\'%s\' does not exist.' % real_path)
+ mime_types = {}
+ for ext, mime_type in options.mime_types:
+ mime_types['.' + ext] = mime_type
+
+ def ShutdownCallback():
+ server.TestingEnded()
+ close_browser = options.tool is not None and not options.interactive
+ return close_browser
+
+ listener = browsertester.rpclistener.RPCListener(ShutdownCallback)
+ server.Configure(file_mapping,
+ dict(options.map_redirects),
+ mime_types,
+ options.allow_404,
+ options.bandwidth,
+ listener,
+ options.serving_dirs)
+
+ browser = browsertester.browserlauncher.ChromeLauncher(options)
+
+ full_url = 'http://%s:%d/%s' % (host, port, url)
+ if len(options.test_args) > 0:
+ full_url += '?' + urllib.urlencode(options.test_args)
+ browser.Run(full_url, port)
+ server.TestingBegun(0.125)
+
+ # In Python 2.5, server.handle_request may block indefinitely. Serving pages
+ # is done in its own thread so the main thread can time out as needed.
+ def Serve():
+ while server.test_in_progress or options.interactive:
+ server.handle_request()
+ thread.start_new_thread(Serve, ())
+
+ tool_failed = False
+ time_started = time.time()
+
+ def HardTimeout(total_time):
+ return total_time >= 0.0 and time.time() - time_started >= total_time
+
+ try:
+ while server.test_in_progress or options.interactive:
+ if not browser.IsRunning():
+ listener.ServerError('Browser process ended during test '
+ '(return code %r)' % browser.GetReturnCode())
+ # If Chrome exits prematurely without making a single request to the
+ # web server, this is probally a Chrome crash-on-launch bug not related
+ # to the test at hand. Retry, unless we're in interactive mode. In
+ # interactive mode the user may manually close the browser, so don't
+ # retry (it would just be annoying.)
+ if not server.received_request and not options.interactive:
+ raise RetryTest('Chrome failed to launch.')
+ else:
+ break
+ elif not options.interactive and server.TimedOut(options.timeout):
+ js_time = server.TimeSinceJSHeartbeat()
+ err = 'Did not hear from the test for %.1f seconds.' % options.timeout
+ err += '\nHeard from Javascript %.1f seconds ago.' % js_time
+ if js_time > 2.0:
+ err += '\nThe renderer probably hung or crashed.'
+ else:
+ err += '\nThe test probably did not get a callback that it expected.'
+ listener.ServerError(err)
+ break
+ elif not options.interactive and HardTimeout(options.hard_timeout):
+ listener.ServerError('The test took over %.1f seconds. This is '
+ 'probably a runaway test.' % options.hard_timeout)
+ break
+ else:
+ # If Python 2.5 support is dropped, stick server.handle_request() here.
+ time.sleep(0.125)
+
+ if options.tool:
+ sys.stdout.write('##################### Waiting for the tool to exit\n')
+ browser.WaitForProcessDeath()
+ sys.stdout.write('##################### Processing tool logs\n')
+ tool_failed = ProcessToolLogs(options, browser.tool_log_dir)
+
+ finally:
+ try:
+ if listener.ever_failed and not options.interactive:
+ if not server.received_request:
+ sys.stdout.write('\nNo URLs were served by the test runner. It is '
+ 'unlikely this test failure has anything to do with '
+ 'this particular test.\n')
+ DumpNetLog(browser.NetLogName())
+ except Exception:
+ listener.ever_failed = 1
+ browser.Cleanup()
+ server.server_close()
+
+ if tool_failed:
+ return 2
+ elif listener.ever_failed:
+ return 1
+ else:
+ return 0
+
+
+# This is an entrypoint for tests that treat the browser tester as a Python
+# library rather than an opaque script.
+# (e.g. run_inbrowser_trusted_crash_in_startup_test)
+def Run(url, options):
+ result = 1
+ attempt = 1
+ while True:
+ try:
+ result = RunTestsOnce(url, options)
+ break
+ except RetryTest:
+ # Only retry once.
+ if attempt < 2:
+ sys.stdout.write('\n@@@STEP_WARNINGS@@@\n')
+ sys.stdout.write('WARNING: suspected flake, retrying test!\n\n')
+ attempt += 1
+ continue
+ else:
+ sys.stdout.write('\nWARNING: failed too many times, not retrying.\n\n')
+ result = 1
+ break
+ return result
+
+
+def RunFromCommandLine():
+ parser = BuildArgParser()
+ options, args = parser.parse_args()
+
+ if len(args) != 0:
+ print args
+ parser.error('Invalid arguments')
+
+ # Validate the URL
+ url = options.url
+ if url is None:
+ parser.error('Must specify a URL')
+
+ return Run(url, options)
+
+
+if __name__ == '__main__':
+ sys.exit(RunFromCommandLine())

Powered by Google App Engine
This is Rietveld 408576698