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

Side by Side Diff: gm/rebaseline_server/server.py

Issue 215503002: rebaseline_server: add --compare-configs option (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: rebase Created 6 years, 8 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
« no previous file with comments | « gm/rebaseline_server/results.py ('k') | gm/rebaseline_server/static/index.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 """ 3 """
4 Copyright 2013 Google Inc. 4 Copyright 2013 Google Inc.
5 5
6 Use of this source code is governed by a BSD-style license that can be 6 Use of this source code is governed by a BSD-style license that can be
7 found in the LICENSE file. 7 found in the LICENSE file.
8 8
9 HTTP server for our HTML rebaseline viewer. 9 HTTP server for our HTML rebaseline viewer.
10 """ 10 """
(...skipping 10 matching lines...) Expand all
21 import socket 21 import socket
22 import subprocess 22 import subprocess
23 import sys 23 import sys
24 import thread 24 import thread
25 import threading 25 import threading
26 import time 26 import time
27 import urlparse 27 import urlparse
28 28
29 # Imports from within Skia 29 # Imports from within Skia
30 # 30 #
31 # We need to add the 'tools' directory, so that we can import svn.py within 31 # We need to add the 'tools' directory for svn.py, and the 'gm' directory for
32 # gm_json.py .
32 # that directory. 33 # that directory.
33 # Make sure that the 'tools' dir is in the PYTHONPATH, but add it at the *end* 34 # Make sure that the 'tools' dir is in the PYTHONPATH, but add it at the *end*
34 # so any dirs that are already in the PYTHONPATH will be preferred. 35 # so any dirs that are already in the PYTHONPATH will be preferred.
35 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) 36 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
36 TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(PARENT_DIRECTORY)) 37 GM_DIRECTORY = os.path.dirname(PARENT_DIRECTORY)
38 TRUNK_DIRECTORY = os.path.dirname(GM_DIRECTORY)
37 TOOLS_DIRECTORY = os.path.join(TRUNK_DIRECTORY, 'tools') 39 TOOLS_DIRECTORY = os.path.join(TRUNK_DIRECTORY, 'tools')
38 if TOOLS_DIRECTORY not in sys.path: 40 if TOOLS_DIRECTORY not in sys.path:
39 sys.path.append(TOOLS_DIRECTORY) 41 sys.path.append(TOOLS_DIRECTORY)
40 import svn 42 import svn
43 if GM_DIRECTORY not in sys.path:
44 sys.path.append(GM_DIRECTORY)
45 import gm_json
41 46
42 # Imports from local dir 47 # Imports from local dir
43 # 48 #
44 # Note: we import results under a different name, to avoid confusion with the 49 # Note: we import results under a different name, to avoid confusion with the
45 # Server.results() property. See discussion at 50 # Server.results() property. See discussion at
46 # https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.p y#newcode44 51 # https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.p y#newcode44
52 import compare_configs
47 import compare_to_expectations 53 import compare_to_expectations
48 import imagepairset 54 import imagepairset
49 import results as results_mod 55 import results as results_mod
50 56
51 PATHSPLIT_RE = re.compile('/([^/]+)/(.+)') 57 PATHSPLIT_RE = re.compile('/([^/]+)/(.+)')
52 58
53 # A simple dictionary of file name extensions to MIME types. The empty string 59 # A simple dictionary of file name extensions to MIME types. The empty string
54 # entry is used as the default when no extension was given or if the extension 60 # entry is used as the default when no extension was given or if the extension
55 # has no entry in this dictionary. 61 # has no entry in this dictionary.
56 MIME_TYPE_MAP = {'': 'application/octet-stream', 62 MIME_TYPE_MAP = {'': 'application/octet-stream',
57 'html': 'text/html', 63 'html': 'text/html',
58 'css': 'text/css', 64 'css': 'text/css',
59 'png': 'image/png', 65 'png': 'image/png',
60 'js': 'application/javascript', 66 'js': 'application/javascript',
61 'json': 'application/json' 67 'json': 'application/json'
62 } 68 }
63 69
64 # Keys that server.py uses to create the toplevel content header. 70 # Keys that server.py uses to create the toplevel content header.
65 # NOTE: Keep these in sync with static/constants.js 71 # NOTE: Keep these in sync with static/constants.js
66 KEY__EDITS__MODIFICATIONS = 'modifications' 72 KEY__EDITS__MODIFICATIONS = 'modifications'
67 KEY__EDITS__OLD_RESULTS_HASH = 'oldResultsHash' 73 KEY__EDITS__OLD_RESULTS_HASH = 'oldResultsHash'
68 KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' 74 KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType'
69 75
70 DEFAULT_ACTUALS_DIR = compare_to_expectations.DEFAULT_ACTUALS_DIR 76 DEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR
71 DEFAULT_ACTUALS_REPO_REVISION = 'HEAD' 77 DEFAULT_ACTUALS_REPO_REVISION = 'HEAD'
72 DEFAULT_ACTUALS_REPO_URL = 'http://skia-autogen.googlecode.com/svn/gm-actual' 78 DEFAULT_ACTUALS_REPO_URL = 'http://skia-autogen.googlecode.com/svn/gm-actual'
73 DEFAULT_PORT = 8888 79 DEFAULT_PORT = 8888
74 80
75 # Directory within which the server will serve out static files. 81 # Directory, relative to PARENT_DIRECTORY, within which the server will serve
76 STATIC_CONTENTS_SUBDIR = 'static' # within PARENT_DIR 82 # out live results (not static files).
77 GENERATED_IMAGES_SUBDIR = 'generated-images' # within STATIC_CONTENTS_SUBDIR 83 RESULTS_SUBDIR = 'results'
84 # Directory, relative to PARENT_DIRECTORY, within which the server will serve
85 # out static files.
86 STATIC_CONTENTS_SUBDIR = 'static'
87 # All of the GENERATED_*_SUBDIRS are relative to STATIC_CONTENTS_SUBDIR
88 GENERATED_HTML_SUBDIR = 'generated-html'
89 GENERATED_IMAGES_SUBDIR = 'generated-images'
90 GENERATED_JSON_SUBDIR = 'generated-json'
78 91
79 # How often (in seconds) clients should reload while waiting for initial 92 # How often (in seconds) clients should reload while waiting for initial
80 # results to load. 93 # results to load.
81 RELOAD_INTERVAL_UNTIL_READY = 10 94 RELOAD_INTERVAL_UNTIL_READY = 10
82 95
96 SUMMARY_TYPES = [
97 results_mod.KEY__HEADER__RESULTS_FAILURES,
98 results_mod.KEY__HEADER__RESULTS_ALL,
99 ]
100 # If --compare-configs is specified, compare these configs.
101 CONFIG_PAIRS_TO_COMPARE = [('8888', 'gpu')]
102
83 _HTTP_HEADER_CONTENT_LENGTH = 'Content-Length' 103 _HTTP_HEADER_CONTENT_LENGTH = 'Content-Length'
84 _HTTP_HEADER_CONTENT_TYPE = 'Content-Type' 104 _HTTP_HEADER_CONTENT_TYPE = 'Content-Type'
85 105
86 _SERVER = None # This gets filled in by main() 106 _SERVER = None # This gets filled in by main()
87 107
88 108
89 def _run_command(args, directory): 109 def _run_command(args, directory):
90 """Runs a command and returns stdout as a single string. 110 """Runs a command and returns stdout as a single string.
91 111
92 Args: 112 Args:
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 checkout already exists) 150 checkout already exists)
131 Returns: an svn.Svn object referring to the local checkout. 151 Returns: an svn.Svn object referring to the local checkout.
132 """ 152 """
133 local_checkout = svn.Svn(dir_path) 153 local_checkout = svn.Svn(dir_path)
134 if not os.path.isdir(dir_path): 154 if not os.path.isdir(dir_path):
135 os.makedirs(dir_path) 155 os.makedirs(dir_path)
136 local_checkout.Checkout(repo_url, '.') 156 local_checkout.Checkout(repo_url, '.')
137 return local_checkout 157 return local_checkout
138 158
139 159
160 def _create_index(file_path, config_pairs):
161 """Creates an index file linking to all results available from this server.
162
163 Prior to https://codereview.chromium.org/215503002 , we had a static
164 index.html within our repo. But now that the results may or may not include
165 config comparisons, index.html needs to be generated differently depending
166 on which results are included.
167
168 TODO(epoger): Instead of including raw HTML within the Python code,
169 consider restoring the index.html file as a template and using django (or
170 similar) to fill in dynamic content.
171
172 Args:
173 file_path: path on local disk to write index to; any directory components
174 of this path that do not already exist will be created
175 config_pairs: what pairs of configs (if any) we compare actual results of
176 """
177 dir_path = os.path.dirname(file_path)
178 if not os.path.isdir(dir_path):
179 os.makedirs(dir_path)
180 with open(file_path, 'w') as file_handle:
181 file_handle.write(
182 '<!DOCTYPE html><html>'
183 '<head><title>rebaseline_server</title></head>'
184 '<body><ul>')
185 if SUMMARY_TYPES:
186 file_handle.write('<li>Expectations vs Actuals</li><ul>')
187 for summary_type in SUMMARY_TYPES:
188 file_handle.write(
189 '<li>'
190 '<a href="/%s/view.html#/view.html?resultsToLoad=/%s/%s">'
191 '%s</a></li>' % (
192 STATIC_CONTENTS_SUBDIR, RESULTS_SUBDIR,
193 summary_type, summary_type))
194 file_handle.write('</ul>')
195 if config_pairs:
196 file_handle.write('<li>Comparing configs within actual results</li><ul>')
197 for config_pair in config_pairs:
198 file_handle.write('<li>%s vs %s:' % config_pair)
199 for summary_type in SUMMARY_TYPES:
200 file_handle.write(
201 ' <a href="/%s/view.html#/view.html?'
202 'resultsToLoad=/%s/%s/%s-vs-%s_%s.json">%s</a>' % (
203 STATIC_CONTENTS_SUBDIR, STATIC_CONTENTS_SUBDIR,
204 GENERATED_JSON_SUBDIR, config_pair[0], config_pair[1],
205 summary_type, summary_type))
206 file_handle.write('</li>')
207 file_handle.write('</ul>')
208 file_handle.write('</ul></body></html>')
209
210
140 class Server(object): 211 class Server(object):
141 """ HTTP server for our HTML rebaseline viewer. """ 212 """ HTTP server for our HTML rebaseline viewer. """
142 213
143 def __init__(self, 214 def __init__(self,
144 actuals_dir=DEFAULT_ACTUALS_DIR, 215 actuals_dir=DEFAULT_ACTUALS_DIR,
145 actuals_repo_revision=DEFAULT_ACTUALS_REPO_REVISION, 216 actuals_repo_revision=DEFAULT_ACTUALS_REPO_REVISION,
146 actuals_repo_url=DEFAULT_ACTUALS_REPO_URL, 217 actuals_repo_url=DEFAULT_ACTUALS_REPO_URL,
147 port=DEFAULT_PORT, export=False, editable=True, 218 port=DEFAULT_PORT, export=False, editable=True,
148 reload_seconds=0): 219 reload_seconds=0, config_pairs=None):
149 """ 220 """
150 Args: 221 Args:
151 actuals_dir: directory under which we will check out the latest actual 222 actuals_dir: directory under which we will check out the latest actual
152 GM results 223 GM results
153 actuals_repo_revision: revision of actual-results.json files to process 224 actuals_repo_revision: revision of actual-results.json files to process
154 actuals_repo_url: SVN repo to download actual-results.json files from; 225 actuals_repo_url: SVN repo to download actual-results.json files from;
155 if None or '', don't fetch new actual-results files at all, 226 if None or '', don't fetch new actual-results files at all,
156 just compare to whatever files are already in actuals_dir 227 just compare to whatever files are already in actuals_dir
157 port: which TCP port to listen on for HTTP requests 228 port: which TCP port to listen on for HTTP requests
158 export: whether to allow HTTP clients on other hosts to access this server 229 export: whether to allow HTTP clients on other hosts to access this server
159 editable: whether HTTP clients are allowed to submit new baselines 230 editable: whether HTTP clients are allowed to submit new baselines
160 reload_seconds: polling interval with which to check for new results; 231 reload_seconds: polling interval with which to check for new results;
161 if 0, don't check for new results at all 232 if 0, don't check for new results at all
233 config_pairs: List of (string, string) tuples; for each tuple, compare
234 actual results of these two configs. If None or empty,
235 don't compare configs at all.
162 """ 236 """
163 self._actuals_dir = actuals_dir 237 self._actuals_dir = actuals_dir
164 self._actuals_repo_revision = actuals_repo_revision 238 self._actuals_repo_revision = actuals_repo_revision
165 self._actuals_repo_url = actuals_repo_url 239 self._actuals_repo_url = actuals_repo_url
166 self._port = port 240 self._port = port
167 self._export = export 241 self._export = export
168 self._editable = editable 242 self._editable = editable
169 self._reload_seconds = reload_seconds 243 self._reload_seconds = reload_seconds
244 self._config_pairs = config_pairs or []
245 _create_index(
246 file_path=os.path.join(
247 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR,
248 "index.html"),
249 config_pairs=config_pairs)
170 if actuals_repo_url: 250 if actuals_repo_url:
171 self._actuals_repo = _create_svn_checkout( 251 self._actuals_repo = _create_svn_checkout(
172 dir_path=actuals_dir, repo_url=actuals_repo_url) 252 dir_path=actuals_dir, repo_url=actuals_repo_url)
173 253
174 # Reentrant lock that must be held whenever updating EITHER of: 254 # Reentrant lock that must be held whenever updating EITHER of:
175 # 1. self._results 255 # 1. self._results
176 # 2. the expected or actual results on local disk 256 # 2. the expected or actual results on local disk
177 self.results_rlock = threading.RLock() 257 self.results_rlock = threading.RLock()
178 # self._results will be filled in by calls to update_results() 258 # self._results will be filled in by calls to update_results()
179 self._results = None 259 self._results = None
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 # 316 #
237 # Because Skia uses depot_tools, we have to update using "gclient sync" 317 # Because Skia uses depot_tools, we have to update using "gclient sync"
238 # instead of raw git (or SVN) update. Happily, this will work whether 318 # instead of raw git (or SVN) update. Happily, this will work whether
239 # the checkout was created using git or SVN. 319 # the checkout was created using git or SVN.
240 if self._reload_seconds: 320 if self._reload_seconds:
241 logging.info( 321 logging.info(
242 'Updating expected GM results in %s by syncing Skia repo ...' % 322 'Updating expected GM results in %s by syncing Skia repo ...' %
243 compare_to_expectations.DEFAULT_EXPECTATIONS_DIR) 323 compare_to_expectations.DEFAULT_EXPECTATIONS_DIR)
244 _run_command(['gclient', 'sync'], TRUNK_DIRECTORY) 324 _run_command(['gclient', 'sync'], TRUNK_DIRECTORY)
245 325
246 self._results = compare_to_expectations.Results( 326 self._results = compare_to_expectations.ExpectationComparisons(
247 actuals_root=self._actuals_dir, 327 actuals_root=self._actuals_dir,
248 generated_images_root=os.path.join( 328 generated_images_root=os.path.join(
249 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, 329 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR,
250 GENERATED_IMAGES_SUBDIR), 330 GENERATED_IMAGES_SUBDIR),
251 diff_base_url=posixpath.join( 331 diff_base_url=posixpath.join(
252 os.pardir, STATIC_CONTENTS_SUBDIR, GENERATED_IMAGES_SUBDIR)) 332 os.pardir, STATIC_CONTENTS_SUBDIR, GENERATED_IMAGES_SUBDIR))
253 333
334 json_dir = os.path.join(
335 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_JSON_SUBDIR)
336 if not os.path.isdir(json_dir):
337 os.makedirs(json_dir)
338
339 for config_pair in self._config_pairs:
340 config_comparisons = compare_configs.ConfigComparisons(
341 configs=config_pair,
342 actuals_root=self._actuals_dir,
343 generated_images_root=os.path.join(
344 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR,
345 GENERATED_IMAGES_SUBDIR),
346 diff_base_url=posixpath.join(
347 os.pardir, GENERATED_IMAGES_SUBDIR))
348 for summary_type in SUMMARY_TYPES:
349 gm_json.WriteToFile(
350 config_comparisons.get_packaged_results_of_type(
351 results_type=summary_type),
352 os.path.join(
353 json_dir, '%s-vs-%s_%s.json' % (
354 config_pair[0], config_pair[1], summary_type)))
355
254 def _result_loader(self, reload_seconds=0): 356 def _result_loader(self, reload_seconds=0):
255 """ Call self.update_results(), either once or periodically. 357 """ Call self.update_results(), either once or periodically.
256 358
257 Params: 359 Params:
258 reload_seconds: integer; if nonzero, reload results at this interval 360 reload_seconds: integer; if nonzero, reload results at this interval
259 (in which case, this method will never return!) 361 (in which case, this method will never return!)
260 """ 362 """
261 self.update_results() 363 self.update_results()
262 logging.info('Initial results loaded. Ready for requests on %s' % self._url) 364 logging.info('Initial results loaded. Ready for requests on %s' % self._url)
263 if reload_seconds: 365 if reload_seconds:
(...skipping 29 matching lines...) Expand all
293 def do_GET(self): 395 def do_GET(self):
294 """ 396 """
295 Handles all GET requests, forwarding them to the appropriate 397 Handles all GET requests, forwarding them to the appropriate
296 do_GET_* dispatcher. 398 do_GET_* dispatcher.
297 399
298 If we see any Exceptions, return a 404. This fixes http://skbug.com/2147 400 If we see any Exceptions, return a 404. This fixes http://skbug.com/2147
299 """ 401 """
300 try: 402 try:
301 logging.debug('do_GET: path="%s"' % self.path) 403 logging.debug('do_GET: path="%s"' % self.path)
302 if self.path == '' or self.path == '/' or self.path == '/index.html' : 404 if self.path == '' or self.path == '/' or self.path == '/index.html' :
303 self.redirect_to('/%s/index.html' % STATIC_CONTENTS_SUBDIR) 405 self.redirect_to('/%s/%s/index.html' % (
406 STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR))
304 return 407 return
305 if self.path == '/favicon.ico' : 408 if self.path == '/favicon.ico' :
306 self.redirect_to('/%s/favicon.ico' % STATIC_CONTENTS_SUBDIR) 409 self.redirect_to('/%s/favicon.ico' % STATIC_CONTENTS_SUBDIR)
307 return 410 return
308 411
309 # All requests must be of this form: 412 # All requests must be of this form:
310 # /dispatcher/remainder 413 # /dispatcher/remainder
311 # where 'dispatcher' indicates which do_GET_* dispatcher to run 414 # where 'dispatcher' indicates which do_GET_* dispatcher to run
312 # and 'remainder' is the remaining path sent to that dispatcher. 415 # and 'remainder' is the remaining path sent to that dispatcher.
313 normpath = posixpath.normpath(self.path) 416 normpath = posixpath.normpath(self.path)
314 (dispatcher_name, remainder) = PATHSPLIT_RE.match(normpath).groups() 417 (dispatcher_name, remainder) = PATHSPLIT_RE.match(normpath).groups()
315 dispatchers = { 418 dispatchers = {
316 'results': self.do_GET_results, 419 RESULTS_SUBDIR: self.do_GET_results,
317 STATIC_CONTENTS_SUBDIR: self.do_GET_static, 420 STATIC_CONTENTS_SUBDIR: self.do_GET_static,
318 } 421 }
319 dispatcher = dispatchers[dispatcher_name] 422 dispatcher = dispatchers[dispatcher_name]
320 dispatcher(remainder) 423 dispatcher(remainder)
321 except: 424 except:
322 self.send_error(404) 425 self.send_error(404)
323 raise 426 raise
324 427
325 def do_GET_results(self, results_type): 428 def do_GET_results(self, results_type):
326 """ Handle a GET request for GM results. 429 """ Handle a GET request for GM results.
327 430
328 Args: 431 Args:
329 results_type: string indicating which set of results to return; 432 results_type: string indicating which set of results to return;
330 must be one of the results_mod.RESULTS_* constants 433 must be one of the results_mod.RESULTS_* constants
331 """ 434 """
332 logging.debug('do_GET_results: sending results of type "%s"' % results_type) 435 logging.debug('do_GET_results: sending results of type "%s"' % results_type)
333 # Since we must make multiple calls to the Results object, grab a 436 # Since we must make multiple calls to the ExpectationComparisons object,
334 # reference to it in case it is updated to point at a new Results 437 # grab a reference to it in case it is updated to point at a new
335 # object within another thread. 438 # ExpectationComparisons object within another thread.
336 # 439 #
337 # TODO(epoger): Rather than using a global variable for the handler 440 # TODO(epoger): Rather than using a global variable for the handler
338 # to refer to the Server object, make Server a subclass of 441 # to refer to the Server object, make Server a subclass of
339 # HTTPServer, and then it could be available to the handler via 442 # HTTPServer, and then it could be available to the handler via
340 # the handler's .server instance variable. 443 # the handler's .server instance variable.
341 results_obj = _SERVER.results 444 results_obj = _SERVER.results
342 if results_obj: 445 if results_obj:
343 response_dict = results_obj.get_packaged_results_of_type( 446 response_dict = results_obj.get_packaged_results_of_type(
344 results_type=results_type, reload_seconds=_SERVER.reload_seconds, 447 results_type=results_type, reload_seconds=_SERVER.reload_seconds,
345 is_editable=_SERVER.is_editable, is_exported=_SERVER.is_exported) 448 is_editable=_SERVER.is_editable, is_exported=_SERVER.is_exported)
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
517 'files from. Defaults to %(default)s ; if set to ' 620 'files from. Defaults to %(default)s ; if set to '
518 'empty string, just compare to actual-results ' 621 'empty string, just compare to actual-results '
519 'already found in ACTUALS_DIR.'), 622 'already found in ACTUALS_DIR.'),
520 default=DEFAULT_ACTUALS_REPO_URL) 623 default=DEFAULT_ACTUALS_REPO_URL)
521 parser.add_argument('--actuals-revision', 624 parser.add_argument('--actuals-revision',
522 help=('revision of actual-results.json files to process. ' 625 help=('revision of actual-results.json files to process. '
523 'Defaults to %(default)s . Beware of setting this ' 626 'Defaults to %(default)s . Beware of setting this '
524 'argument in conjunction with --editable; you ' 627 'argument in conjunction with --editable; you '
525 'probably only want to edit results at HEAD.'), 628 'probably only want to edit results at HEAD.'),
526 default=DEFAULT_ACTUALS_REPO_REVISION) 629 default=DEFAULT_ACTUALS_REPO_REVISION)
630 parser.add_argument('--compare-configs', action='store_true',
631 help=('In addition to generating differences between '
632 'expectations and actuals, also generate '
633 'differences between these config pairs: '
634 + str(CONFIG_PAIRS_TO_COMPARE)))
527 parser.add_argument('--editable', action='store_true', 635 parser.add_argument('--editable', action='store_true',
528 help=('Allow HTTP clients to submit new baselines.')) 636 help=('Allow HTTP clients to submit new baselines.'))
529 parser.add_argument('--export', action='store_true', 637 parser.add_argument('--export', action='store_true',
530 help=('Instead of only allowing access from HTTP clients ' 638 help=('Instead of only allowing access from HTTP clients '
531 'on localhost, allow HTTP clients on other hosts ' 639 'on localhost, allow HTTP clients on other hosts '
532 'to access this server. WARNING: doing so will ' 640 'to access this server. WARNING: doing so will '
533 'allow users on other hosts to modify your ' 641 'allow users on other hosts to modify your '
534 'GM expectations, if combined with --editable.')) 642 'GM expectations, if combined with --editable.'))
535 parser.add_argument('--port', type=int, 643 parser.add_argument('--port', type=int,
536 help=('Which TCP port to listen on for HTTP requests; ' 644 help=('Which TCP port to listen on for HTTP requests; '
537 'defaults to %(default)s'), 645 'defaults to %(default)s'),
538 default=DEFAULT_PORT) 646 default=DEFAULT_PORT)
539 parser.add_argument('--reload', type=int, 647 parser.add_argument('--reload', type=int,
540 help=('How often (a period in seconds) to update the ' 648 help=('How often (a period in seconds) to update the '
541 'results. If specified, both expected and actual ' 649 'results. If specified, both expected and actual '
542 'results will be updated by running "gclient sync" ' 650 'results will be updated by running "gclient sync" '
543 'on your Skia checkout as a whole. ' 651 'on your Skia checkout as a whole. '
544 'By default, we do not reload at all, and you ' 652 'By default, we do not reload at all, and you '
545 'must restart the server to pick up new data.'), 653 'must restart the server to pick up new data.'),
546 default=0) 654 default=0)
547 args = parser.parse_args() 655 args = parser.parse_args()
656 if args.compare_configs:
657 config_pairs = CONFIG_PAIRS_TO_COMPARE
658 else:
659 config_pairs = None
660
548 global _SERVER 661 global _SERVER
549 _SERVER = Server(actuals_dir=args.actuals_dir, 662 _SERVER = Server(actuals_dir=args.actuals_dir,
550 actuals_repo_revision=args.actuals_revision, 663 actuals_repo_revision=args.actuals_revision,
551 actuals_repo_url=args.actuals_repo, 664 actuals_repo_url=args.actuals_repo,
552 port=args.port, export=args.export, editable=args.editable, 665 port=args.port, export=args.export, editable=args.editable,
553 reload_seconds=args.reload) 666 reload_seconds=args.reload, config_pairs=config_pairs)
554 _SERVER.run() 667 _SERVER.run()
555 668
556 669
557 if __name__ == '__main__': 670 if __name__ == '__main__':
558 main() 671 main()
OLDNEW
« no previous file with comments | « gm/rebaseline_server/results.py ('k') | gm/rebaseline_server/static/index.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698