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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 """Automate the setup process of Chrome Endure environment.
2
3 Usage:
4 ./endure.py [fetch|server] [option]
5
6 Command "fetch" will automatically setup Chrome Endure environment.
7 The script will do the following things:
8 1) Set up depot_tool if necessary.
9 2) Check out python code which Chrome Endure depends on.
10 3) Check out pre-built binary of Chrome, PyAuto lib and chrome driver
11 4) Check out chrome graphing file which are needed to show the test results.
12
13 After fetching Chrome Endure, you should be able to run the endure test. Try:
14 TEST_LENGTH=60 LOCAL_GRAPH='./chrome_graph'
15 python ./src/chrome/test/functional/perf_endure.py
16 perf_endure.ChromeEndureGmailTest.testGmailComposeDiscard
17 The above commands runs an Chrome Endure test for 60 seconds and saves
18 the results to ./chrome_graph. LOCAL_GRAPH is the directory where you have
19 chrome graphing files. By default, it is <DIR_OF_THIS_SCIRPT>/chrome_endure.
20
21 Command "server" will start a local HTTP server to serve the directory which
22 contains the result data files. Run this command after you have test results
23 output to <DIR_OF_THIS_SCRIPT>/chrome_endure and a server will be started with
24 a port that is automatically picked. You can then view the result graphs
25 via http://localhost:<GIVEN_PORT>.
26
27 Use ./endure [fetch|server] --help for more options.
28 """
29
30 import BaseHTTPServer
31 import optparse
32 import os
33 import platform
34 import shutil
35 import SimpleHTTPServer
36 import subprocess
37 import sys
38 import urllib
39 import urllib2
40 import zipfile
41
42
43 class SetupError(Exception):
44 """Catch errors in setting up Chrome Endure."""
45
46 def __init__(self, label, message):
47 super(SetupError, self).__init__()
48 self.label = label
49 self.message = message
50
51 def __str__(self):
52 return self.label+'\n'+str(self.message)
53
54
55 class PlainHelpFormatter(optparse.IndentedHelpFormatter):
56 """Format the help message of this script."""
57
58 def format_description(self, description):
59 if description:
60 return description + '\n'
61 else:
62 return ''
63
64
65 class CmdFetch(object):
66 """Fetch Chrome Endure.
67
68 Usage:
69 ./endure.py fetch [options]
70 Examples:
71 ./endure.py fetch
72 Fetch latest version of Chrome Endure to the same directory as this script.
73
74 ./endure.py fetch --endure-dir=/home/user/endure_dir --revision=18043
75 Fetch revision 18043 to /home/user/endure_dir
76 """
77 _URLS = {'depot_tools': ('http://src.chromium.org'
78 '/chrome/trunk/tools/depot_tools'),
79 'pyauto': ('https://src.chromium.org/'
80 'chrome/trunk/src/chrome/test/functional.DEPS'),
81 'binary': ('http://commondatastorage.googleapis.com/'
82 'chromium-browser-continuous/{os_type}/{revision}'),
83 # TODO(fdeng): change to an external address after graph code
84 # is checked in
85 'graph': ('http://www.corp.google.com/'
86 '~dennisjeffrey/chrome_perf/local_graphs.zip'),
87 }
88 NAME = 'fetch'
89 DESCRIPTION = 'Fetch Chrome Endure.'
90
91 def _ParseArgs(self, argv):
92 parser = optparse.OptionParser(
93 usage='%%prog %s [options]' % self.NAME,
94 formatter=PlainHelpFormatter(),
95 description=self.__doc__)
96 parser.add_option(
97 '-d', '--endure-dir', type='string', default=os.path.dirname(__file__),
98 help='Directory in which to setup or update.')
99 parser.add_option(
100 '-r', '--revision', type='string', default=None,
101 help='Revision of Chrome tree to get Chrome Endure.')
102 return parser.parse_args(argv)
103
104 def Run(self, argv):
105 """Run this command."""
106 options, _ = self._ParseArgs(argv)
107 self._endure_dir = os.path.abspath(options.endure_dir)
108 self._revision = options.revision
109 if not self._revision:
110 self._revision = self._GetLatestRevision(self._GetCurrentOSType())
111 self._os_type = self._GetCurrentOSType()
112 self._depot_dir = os.path.join(self._endure_dir, 'depot_tools')
113 self._gclient = os.path.join(self._depot_dir, 'gclient')
114 self._fetch_py = os.path.join(self._endure_dir, 'src', 'chrome',
115 'test', 'pyautolib',
116 'fetch_prebuilt_pyauto.py')
117 self._binary_dir = os.path.join(self._endure_dir, 'src', 'out', 'Release')
118 self._graph_dir = os.path.join(self._endure_dir, 'chrome_graph')
119
120 if not os.path.isdir(self._endure_dir):
121 os.makedirs(self._endure_dir)
122
123 print 'Checking depot tools...'
124 self._FetchDepot()
125 print 'Fetching PyAuto (python code)...'
126 self._FetchPyAuto(self._revision)
127 print 'Fetching binaries(chrome, pyautolib, chrome driver)...'
128 self._FetchBinaries(self._os_type, self._revision)
129 # TODO(fdeng): remove this after it is check into the chrome tree.
130 print 'Fetching chrome graphing files...'
131 self._FetchGraph()
132 return 0
133
134 def _FetchDepot(self):
135 """Fetch depot_tool if not installed in the system."""
136 try:
137 subprocess.call(['gclient', '--version'])
138 self._gclient = 'gclient'
139 except OSError:
140 try:
141 # TODO(fdeng): how can I know gclient is not installed
142 # without checking OSError, a better way?
143 url = self._URLS['depot_tools']
144 print 'Fetching depot tools: %s' % url
145 subprocess.call(['svn', 'co', self._URLS['depot_tools'],
146 self._depot_dir])
147 subprocess.call([self._gclient, '--version'])
148 except Exception as e:
149 raise SetupError('Unable to set up depot tools:', str(e))
150
151 def _FetchPyAuto(self, revision):
152 """Use gclient to fetch python code."""
153 cur_dir = os.getcwd()
154 try:
155 os.chdir(self._endure_dir)
156 # gclient config
157 config_cmd = [self._gclient, 'config', self._URLS['pyauto']]
158 code = subprocess.call(config_cmd)
159 if code != 0:
160 raise Exception('Running "%s" failed.' % ' '.join(config_cmd))
161 # gclient sync
162 sync_cmd = [self._gclient, 'sync']
163 if revision:
164 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
165 code = subprocess.call(sync_cmd)
166 if code != 0:
167 raise Exception('Running "%s" failed.' % ' '.join(sync_cmd))
168 except Exception as e:
169 raise SetupError('Unable to fetch PyAuto code', str(e))
170 finally:
171 os.chdir(cur_dir)
172
173 def _FetchBinaries(self, os_type, revision):
174 """Get the prebuilt binaries from continuous build archive."""
175 try:
176 if not os.path.exists(self._fetch_py):
177 raise Exception(
178 'Unable to find %s, did fetching python code succeed?'
179 % self._fetch_py)
180 print 'Cleaning %s' % self._binary_dir
181 if os.path.exists(self._binary_dir):
182 shutil.rmtree(self._binary_dir)
183 print 'Downloading...'
184 cmd = [self._fetch_py, '-d', self._binary_dir,
185 self._URLS['binary'].format(os_type=os_type, revision=revision)]
186 code = subprocess.call(cmd)
187 if code != 0:
188 raise Exception('Running "%s" failed.' % ' '.join(cmd))
189 except Exception as e:
190 raise SetupError(
191 'Unable to fetch binaries for chrome/pyauto/chrome_driver:', str(e))
192
193 def _FetchGraph(self):
194 """Fetch graph code."""
195 try:
196 graph_zip = urllib.urlretrieve(self._URLS['graph'])[0]
197 if os.path.exists(self._graph_dir):
198 print 'Cleaning %s, ' % self._graph_dir,
199 print 'data files(.dat) will be preserved.'
200 files = os.listdir(self._graph_dir)
201 for f in files:
202 path = os.path.join(self._graph_dir, f)
203 if os.path.isdir(path):
204 shutil.rmtree(path)
205 elif os.path.splitext(f) != 'dat':
206 os.remove(path)
207 else:
208 os.mkdir(self._graph_dir)
209 self._UnzipFilenameToDir(graph_zip, self._graph_dir)
210 except Exception as e:
211 raise SetupError('Unable to fetch graph files:', str(e))
212
213 def _GetCurrentOSType(self):
214 """Get a string representation for the current os.
215
216 Returns:
217 'Mac', 'Win', 'Linux', or 'Linux_64'
218
219 Raises:
220 RuntimeError: if os can't be identified.
221 """
222 if sys.platform == 'darwin':
223 os_type = 'Mac'
224 if sys.platform == 'win32':
225 os_type = 'Win'
226 if sys.platform.startswith('linux'):
227 os_type = 'Linux'
228 if platform.architecture()[0] == '64bit':
229 os_type += '_x64'
230 else:
231 raise RuntimeError('Unknown platform')
232 return os_type
233
234 def _GetLatestRevision(self, os_type):
235 """Figure out the latest revision number of the prebuilt binary archive.
236
237 Args:
238 os_type: 'Mac', 'Win', 'Linux', or 'Linux_64'
239
240 Returns:
241 A string of latest revision number or None on fail.
242 """
243 last_change_url = ('http://commondatastorage.googleapis.com/'
244 'chromium-browser-continuous/%s/LAST_CHANGE' % os_type)
245 response = urllib2.urlopen(last_change_url)
246 last_change = response.read()
247 if not last_change:
248 print 'Unable to get latest revision number from %s' % last_change_url
249 return None
250 return last_change
251
252 @classmethod
253 def _UnzipFilenameToDir(cls, filename, directory):
254 """Unzip |filename| to directory |directory|.
255
256 This works with as low as python2.4 (used on win).
257 """
258 zf = zipfile.ZipFile(filename)
259 pushd = os.getcwd()
260 if not os.path.isdir(directory):
261 os.mkdir(directory)
262 os.chdir(directory)
263 # Extract files.
264 for info in zf.infolist():
265 name = info.filename
266 print name
267 if name.endswith('/'): # dir
268 if not os.path.isdir(name):
269 os.makedirs(name)
270 else: # file
271 directory = os.path.dirname(name)
272 if directory and not os.path.isdir(directory):
273 os.makedirs(directory)
274 out = open(name, 'wb')
275 out.write(zf.read(name))
276 out.close()
277 # Set permissions. Permission info in external_attr is shifted 16 bits.
278 os.chmod(name, info.external_attr >> 16L)
279 os.chdir(pushd)
280
281
282 class CmdServer(object):
283 """Start an http server which serves the Chrome Endure test results.
284
285 Usage:
286 ./endure server [options]
287 Examples:
288 ./endure.py server
289 By default, it will serve the directory <DIR_OF_THIS_SCIRPT>/chrome_graph
290 where DIR_OF_THIS_SCIRPT is the directory of this script (endure.py).
291
292 ./endure.py server --graph-dir=/home/user/Document/graph_dir
293 Use --graph-dir if you have your graph directory at a different place.
294 """
295 NAME = 'server'
296 DESCRIPTION = 'Start an http server for viewing the' \
297 'tests results from a browser.'
298
299 def _ParseArgs(self, argv):
300 parser = optparse.OptionParser(
301 usage='%%prog %s [options]' % self.NAME,
302 formatter=PlainHelpFormatter(),
303 description=self.__doc__)
304 parser.add_option(
305 '-g', '--graph-dir', type='string',
306 default=os.path.join(os.path.dirname(__file__), 'chrome_graph'),
307 help='The directory that contains graphing files' \
308 'and data files of test results')
309 return parser.parse_args(argv)
310
311 def Run(self, argv):
312 """Run this command."""
313 options, _ = self._ParseArgs(argv)
314 self._graph_dir = os.path.abspath(options.graph_dir)
315 cur_dir = os.getcwd()
316 os.chdir(self._graph_dir)
317 httpd = BaseHTTPServer.HTTPServer(('', 0),
318 EndureHTTPRequestHandler)
319 try:
320 print 'Serving %s at port %d' % (self._graph_dir, httpd.server_port)
321 print 'View test results at http://localhost:%d' % httpd.server_port
322 print 'Press Ctrl-C to stop the server.'
323 httpd.serve_forever()
324 except KeyboardInterrupt:
325 print 'Shutting down ...'
326 httpd.shutdown()
327 finally:
328 os.chdir(cur_dir)
329 return 0
330
331
332 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
333 """A simple HTTP request handler for showing Chrome Endure test results."""
334
335 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
336 """Override send_head so it won't redirect if path is not ended with "/".
337
338 Common code for GET and HEAD commands. This sends the response
339 code and MIME headers.
340
341 Returns:
342 either a file object (which has to be copied
343 to the outputfile by the caller unless the command was HEAD,
344 and must be closed by the caller under all circumstances), or
345 None, in which case the caller has nothing further to do.
346 """
347 path = self.translate_path(self.path)
348 f = None
349 if os.path.isdir(path):
350 for index in 'index.html', 'index.htm':
351 index = os.path.join(path, index)
352 if os.path.exists(index):
353 path = index
354 break
355 else:
356 return self.list_directory(path)
357 ctype = self.guess_type(path)
358 try:
359 # Always read in binary mode. Opening files in text mode may cause
360 # newline translations, making the actual size of the content
361 # transmitted *less* than the content-length!
362 f = open(path, 'rb')
363 except IOError:
364 self.send_error(404, 'File not found')
365 return None
366 self.send_response(200)
367 self.send_header('Content-type', ctype)
368 fs = os.fstat(f.fileno())
369 self.send_header('Content-Length', str(fs[6]))
370 self.send_header('Last-Modified', self.date_time_string(fs.st_mtime))
371 self.end_headers()
372 return f
373
374
375 class CmdHelp(object):
376 """Print a list of commands or help for a specific command."""
377 NAME = 'help'
378 DESCRIPTION = __doc__
379
380 def __init__(self, cmds):
381 """Initialize help command.
382
383 Args:
384 cmds: Commands for which help information will be printed.
385 """
386 self.cmds = cmds
387
388 def Run(self, args):
389 """Run this command."""
390 if len(args) == 1:
391 return Main(args + ['--help'])
392 print 'Usage:\n ./endure.py [options] command'
393 print 'Commands are:'
394 for cmd in self.cmds:
395 print '\t%s\t\t%s' % (cmd.NAME, cmd.DESCRIPTION)
396
397 print 'Examples:'
398 print ' ./endure.py %s' % CmdFetch.NAME
399 return 0
400
401
402 def Main(argv):
403 if argv and argv[0] == CmdFetch.NAME:
404 command = CmdFetch()
405 elif argv and argv[0] == CmdServer.NAME:
406 command = CmdServer()
407 else:
408 cmds = [CmdFetch, CmdServer, CmdHelp]
409 command = CmdHelp(cmds)
410 return command.Run(argv[1:])
411
412
413 if '__main__' == __name__:
414 sys.exit(Main(sys.argv[1:]))
OLDNEW
« 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