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

Unified Diff: chrome/test/functional/perf/endure_setup.py

Issue 10837114: Automate Chrome Endure setup process. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 4 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/test/functional/perf/endure_setup.py
diff --git a/chrome/test/functional/perf/endure_setup.py b/chrome/test/functional/perf/endure_setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..1a87f1f6760d320e0fabf2e94c40a2fa32b7a39b
--- /dev/null
+++ b/chrome/test/functional/perf/endure_setup.py
@@ -0,0 +1,414 @@
+"""Automate the setup process of Chrome Endure environment.
+
+Usage:
+ ./endure.py [fetch|server] [option]
+
+Command "fetch" will automatically setup Chrome Endure environment.
+The script will do the following things:
+ 1) Set up depot_tool if necessary.
+ 2) Check out python code which Chrome Endure depends on.
+ 3) Check out pre-built binary of Chrome, PyAuto lib and chrome driver
+ 4) Check out chrome graphing file which are needed to show the test results.
+
+After fetching Chrome Endure, you should be able to run the endure test. Try:
+ TEST_LENGTH=60 LOCAL_GRAPH='./chrome_graph'
+ python ./src/chrome/test/functional/perf_endure.py
+ perf_endure.ChromeEndureGmailTest.testGmailComposeDiscard
+The above commands runs an Chrome Endure test for 60 seconds and saves
+the results to ./chrome_graph. LOCAL_GRAPH is the directory where you have
+chrome graphing files. By default, it is <DIR_OF_THIS_SCIRPT>/chrome_endure.
+
+Command "server" will start a local HTTP server to serve the directory which
+contains the result data files. Run this command after you have test results
+output to <DIR_OF_THIS_SCRIPT>/chrome_endure and a server will be started with
+a port that is automatically picked. You can then view the result graphs
+via http://localhost:<GIVEN_PORT>.
+
+Use ./endure [fetch|server] --help for more options.
+"""
+
+import BaseHTTPServer
+import optparse
+import os
+import platform
+import shutil
+import SimpleHTTPServer
+import subprocess
+import sys
+import urllib
+import urllib2
+import zipfile
+
+
+class SetupError(Exception):
+ """Catch errors in setting up Chrome Endure."""
+
+ def __init__(self, label, message):
+ super(SetupError, self).__init__()
+ self.label = label
+ self.message = message
+
+ def __str__(self):
+ return self.label+'\n'+str(self.message)
+
+
+class PlainHelpFormatter(optparse.IndentedHelpFormatter):
+ """Format the help message of this script."""
+
+ def format_description(self, description):
+ if description:
+ return description + '\n'
+ else:
+ return ''
+
+
+class CmdFetch(object):
+ """Fetch Chrome Endure.
+
+ Usage:
+ ./endure.py fetch [options]
+ Examples:
+ ./endure.py fetch
+ Fetch latest version of Chrome Endure to the same directory as this script.
+
+ ./endure.py fetch --endure-dir=/home/user/endure_dir --revision=18043
+ Fetch revision 18043 to /home/user/endure_dir
+ """
+ _URLS = {'depot_tools': ('http://src.chromium.org'
+ '/chrome/trunk/tools/depot_tools'),
+ 'pyauto': ('https://src.chromium.org/'
+ 'chrome/trunk/src/chrome/test/functional.DEPS'),
+ 'binary': ('http://commondatastorage.googleapis.com/'
+ 'chromium-browser-continuous/{os_type}/{revision}'),
+ # TODO(fdeng): change to an external address after graph code
+ # is checked in
+ 'graph': ('http://www.corp.google.com/'
+ '~dennisjeffrey/chrome_perf/local_graphs.zip'),
+ }
+ NAME = 'fetch'
+ DESCRIPTION = 'Fetch Chrome Endure.'
+
+ def _ParseArgs(self, argv):
+ parser = optparse.OptionParser(
+ usage='%%prog %s [options]' % self.NAME,
+ formatter=PlainHelpFormatter(),
+ description=self.__doc__)
+ parser.add_option(
+ '-d', '--endure-dir', type='string', default=os.path.dirname(__file__),
+ help='Directory in which to setup or update.')
+ parser.add_option(
+ '-r', '--revision', type='string', default=None,
+ help='Revision of Chrome tree to get Chrome Endure.')
+ return parser.parse_args(argv)
+
+ def Run(self, argv):
+ """Run this command."""
+ options, _ = self._ParseArgs(argv)
+ self._endure_dir = os.path.abspath(options.endure_dir)
+ self._revision = options.revision
+ if not self._revision:
+ self._revision = self._GetLatestRevision(self._GetCurrentOSType())
+ self._os_type = self._GetCurrentOSType()
+ self._depot_dir = os.path.join(self._endure_dir, 'depot_tools')
+ self._gclient = os.path.join(self._depot_dir, 'gclient')
+ self._fetch_py = os.path.join(self._endure_dir, 'src', 'chrome',
+ 'test', 'pyautolib',
+ 'fetch_prebuilt_pyauto.py')
+ self._binary_dir = os.path.join(self._endure_dir, 'src', 'out', 'Release')
+ self._graph_dir = os.path.join(self._endure_dir, 'chrome_graph')
+
+ if not os.path.isdir(self._endure_dir):
+ os.makedirs(self._endure_dir)
+
+ print 'Checking depot tools...'
+ self._FetchDepot()
+ print 'Fetching PyAuto (python code)...'
+ self._FetchPyAuto(self._revision)
+ print 'Fetching binaries(chrome, pyautolib, chrome driver)...'
+ self._FetchBinaries(self._os_type, self._revision)
+ # TODO(fdeng): remove this after it is check into the chrome tree.
+ print 'Fetching chrome graphing files...'
+ self._FetchGraph()
+ return 0
+
+ def _FetchDepot(self):
+ """Fetch depot_tool if not installed in the system."""
+ try:
+ subprocess.call(['gclient', '--version'])
+ self._gclient = 'gclient'
+ except OSError:
+ try:
+ # TODO(fdeng): how can I know gclient is not installed
+ # without checking OSError, a better way?
+ url = self._URLS['depot_tools']
+ print 'Fetching depot tools: %s' % url
+ subprocess.call(['svn', 'co', self._URLS['depot_tools'],
+ self._depot_dir])
+ subprocess.call([self._gclient, '--version'])
+ except Exception as e:
+ raise SetupError('Unable to set up depot tools:', str(e))
+
+ def _FetchPyAuto(self, revision):
+ """Use gclient to fetch python code."""
+ cur_dir = os.getcwd()
+ try:
+ os.chdir(self._endure_dir)
+ # gclient config
+ config_cmd = [self._gclient, 'config', self._URLS['pyauto']]
+ code = subprocess.call(config_cmd)
+ if code != 0:
+ raise Exception('Running "%s" failed.' % ' '.join(config_cmd))
+ # gclient sync
+ sync_cmd = [self._gclient, 'sync']
+ if revision:
+ sync_cmd.extend(['--revision', 'functional.DEPS@'+(revision)])
fdeng1 2012/08/07 22:19:45 Here the script tries to run "gclient sync --revis
dennis_jeffrey 2012/08/08 23:41:07 As discussed offline, I think it'll be fine for no
+ code = subprocess.call(sync_cmd)
+ if code != 0:
+ raise Exception('Running "%s" failed.' % ' '.join(sync_cmd))
+ except Exception as e:
+ raise SetupError('Unable to fetch PyAuto code', str(e))
+ finally:
+ os.chdir(cur_dir)
+
+ def _FetchBinaries(self, os_type, revision):
+ """Get the prebuilt binaries from continuous build archive."""
+ try:
+ if not os.path.exists(self._fetch_py):
+ raise Exception(
+ 'Unable to find %s, did fetching python code succeed?'
+ % self._fetch_py)
+ print 'Cleaning %s' % self._binary_dir
+ if os.path.exists(self._binary_dir):
+ shutil.rmtree(self._binary_dir)
+ print 'Downloading...'
+ cmd = [self._fetch_py, '-d', self._binary_dir,
+ self._URLS['binary'].format(os_type=os_type, revision=revision)]
+ code = subprocess.call(cmd)
+ if code != 0:
+ raise Exception('Running "%s" failed.' % ' '.join(cmd))
+ except Exception as e:
+ raise SetupError(
+ 'Unable to fetch binaries for chrome/pyauto/chrome_driver:', str(e))
+
+ def _FetchGraph(self):
+ """Fetch graph code."""
+ try:
+ graph_zip = urllib.urlretrieve(self._URLS['graph'])[0]
+ if os.path.exists(self._graph_dir):
+ print 'Cleaning %s, ' % self._graph_dir,
+ print 'data files(.dat) will be preserved.'
+ files = os.listdir(self._graph_dir)
+ for f in files:
+ path = os.path.join(self._graph_dir, f)
+ if os.path.isdir(path):
+ shutil.rmtree(path)
+ elif os.path.splitext(f) != 'dat':
+ os.remove(path)
+ else:
+ os.mkdir(self._graph_dir)
+ self._UnzipFilenameToDir(graph_zip, self._graph_dir)
+ except Exception as e:
+ raise SetupError('Unable to fetch graph files:', str(e))
+
+ def _GetCurrentOSType(self):
+ """Get a string representation for the current os.
+
+ Returns:
+ 'Mac', 'Win', 'Linux', or 'Linux_64'
+
+ Raises:
+ RuntimeError: if os can't be identified.
+ """
+ if sys.platform == 'darwin':
+ os_type = 'Mac'
+ if sys.platform == 'win32':
+ os_type = 'Win'
+ if sys.platform.startswith('linux'):
+ os_type = 'Linux'
+ if platform.architecture()[0] == '64bit':
+ os_type += '_x64'
+ else:
+ raise RuntimeError('Unknown platform')
+ return os_type
+
+ def _GetLatestRevision(self, os_type):
+ """Figure out the latest revision number of the prebuilt binary archive.
+
+ Args:
+ os_type: 'Mac', 'Win', 'Linux', or 'Linux_64'
+
+ Returns:
+ A string of latest revision number or None on fail.
+ """
+ last_change_url = ('http://commondatastorage.googleapis.com/'
+ 'chromium-browser-continuous/%s/LAST_CHANGE' % os_type)
+ response = urllib2.urlopen(last_change_url)
+ last_change = response.read()
+ if not last_change:
+ print 'Unable to get latest revision number from %s' % last_change_url
+ return None
+ return last_change
+
+ @classmethod
+ def _UnzipFilenameToDir(cls, filename, directory):
+ """Unzip |filename| to directory |directory|.
+
+ This works with as low as python2.4 (used on win).
+ """
+ zf = zipfile.ZipFile(filename)
+ pushd = os.getcwd()
+ if not os.path.isdir(directory):
+ os.mkdir(directory)
+ os.chdir(directory)
+ # Extract files.
+ for info in zf.infolist():
+ name = info.filename
+ print name
+ if name.endswith('/'): # dir
+ if not os.path.isdir(name):
+ os.makedirs(name)
+ else: # file
+ directory = os.path.dirname(name)
+ if directory and not os.path.isdir(directory):
+ os.makedirs(directory)
+ out = open(name, 'wb')
+ out.write(zf.read(name))
+ out.close()
+ # Set permissions. Permission info in external_attr is shifted 16 bits.
+ os.chmod(name, info.external_attr >> 16L)
+ os.chdir(pushd)
+
+
+class CmdServer(object):
+ """Start an http server which serves the Chrome Endure test results.
+
+ Usage:
+ ./endure server [options]
+ Examples:
+ ./endure.py server
+ By default, it will serve the directory <DIR_OF_THIS_SCIRPT>/chrome_graph
+ where DIR_OF_THIS_SCIRPT is the directory of this script (endure.py).
+
+ ./endure.py server --graph-dir=/home/user/Document/graph_dir
+ Use --graph-dir if you have your graph directory at a different place.
+ """
+ NAME = 'server'
+ DESCRIPTION = 'Start an http server for viewing the' \
+ 'tests results from a browser.'
+
+ def _ParseArgs(self, argv):
+ parser = optparse.OptionParser(
+ usage='%%prog %s [options]' % self.NAME,
+ formatter=PlainHelpFormatter(),
+ description=self.__doc__)
+ parser.add_option(
+ '-g', '--graph-dir', type='string',
+ default=os.path.join(os.path.dirname(__file__), 'chrome_graph'),
+ help='The directory that contains graphing files' \
+ 'and data files of test results')
+ return parser.parse_args(argv)
+
+ def Run(self, argv):
+ """Run this command."""
+ options, _ = self._ParseArgs(argv)
+ self._graph_dir = os.path.abspath(options.graph_dir)
+ cur_dir = os.getcwd()
+ os.chdir(self._graph_dir)
+ httpd = BaseHTTPServer.HTTPServer(('', 0),
+ EndureHTTPRequestHandler)
+ try:
+ print 'Serving %s at port %d' % (self._graph_dir, httpd.server_port)
+ print 'View test results at http://localhost:%d' % httpd.server_port
+ print 'Press Ctrl-C to stop the server.'
+ httpd.serve_forever()
+ except KeyboardInterrupt:
+ print 'Shutting down ...'
+ httpd.shutdown()
+ finally:
+ os.chdir(cur_dir)
+ return 0
+
+
+class EndureHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
fdeng1 2012/08/07 22:09:43 I tried to reuse the TestServer in pyautolib.py. I
dennis_jeffrey 2012/08/08 23:41:07 I think it's fine to have our own basic test serve
+ """A simple HTTP request handler for showing Chrome Endure test results."""
+
+ def send_head(self):
fdeng1 2012/08/07 22:09:43 I wish I didn't have to override this function. Bu
dennis_jeffrey 2012/08/08 23:41:07 As discussed offline, let's no longer override thi
fdeng1 2012/08/09 03:26:16 I am able to solve the problem by modifying the se
dennis_jeffrey 2012/08/09 17:16:27 Great! I made the change on the live chrome endur
+ """Override send_head so it won't redirect if path is not ended with "/".
+
+ Common code for GET and HEAD commands. This sends the response
+ code and MIME headers.
+
+ Returns:
+ either a file object (which has to be copied
+ to the outputfile by the caller unless the command was HEAD,
+ and must be closed by the caller under all circumstances), or
+ None, in which case the caller has nothing further to do.
+ """
+ path = self.translate_path(self.path)
+ f = None
+ if os.path.isdir(path):
+ for index in 'index.html', 'index.htm':
+ index = os.path.join(path, index)
+ if os.path.exists(index):
+ path = index
+ break
+ else:
+ return self.list_directory(path)
+ ctype = self.guess_type(path)
+ try:
+ # Always read in binary mode. Opening files in text mode may cause
+ # newline translations, making the actual size of the content
+ # transmitted *less* than the content-length!
+ f = open(path, 'rb')
+ except IOError:
+ self.send_error(404, 'File not found')
+ return None
+ self.send_response(200)
+ self.send_header('Content-type', ctype)
+ fs = os.fstat(f.fileno())
+ self.send_header('Content-Length', str(fs[6]))
+ self.send_header('Last-Modified', self.date_time_string(fs.st_mtime))
+ self.end_headers()
+ return f
+
+
+class CmdHelp(object):
+ """Print a list of commands or help for a specific command."""
+ NAME = 'help'
+ DESCRIPTION = __doc__
+
+ def __init__(self, cmds):
+ """Initialize help command.
+
+ Args:
+ cmds: Commands for which help information will be printed.
+ """
+ self.cmds = cmds
+
+ def Run(self, args):
+ """Run this command."""
+ if len(args) == 1:
+ return Main(args + ['--help'])
+ print 'Usage:\n ./endure.py [options] command'
+ print 'Commands are:'
+ for cmd in self.cmds:
+ print '\t%s\t\t%s' % (cmd.NAME, cmd.DESCRIPTION)
+
+ print 'Examples:'
+ print ' ./endure.py %s' % CmdFetch.NAME
+ return 0
+
+
+def Main(argv):
+ if argv and argv[0] == CmdFetch.NAME:
+ command = CmdFetch()
+ elif argv and argv[0] == CmdServer.NAME:
+ command = CmdServer()
+ else:
+ cmds = [CmdFetch, CmdServer, CmdHelp]
+ command = CmdHelp(cmds)
+ return command.Run(argv[1:])
+
+
+if '__main__' == __name__:
+ sys.exit(Main(sys.argv[1:]))
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698