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

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: 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
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')]
epoger 2014/03/27 21:31:07 Someday the pairs will be configurable from the co
rmistry 2014/03/28 18:51:39 Acknowledged.
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.
epoger 2014/03/27 21:31:07 I used to have an index.html file checked in (you'
rmistry 2014/03/28 18:51:39 Acknowledged.
162
163 Args:
164 file_path: path on local disk to write index to; any directory components
165 of this path that do not already exist will be created
166 config_pairs: what pairs of configs (if any) we compare actual results of
167 """
168 dir_path = os.path.dirname(file_path)
169 if not os.path.isdir(dir_path):
170 os.makedirs(dir_path)
171 with open(file_path, 'w') as file_handle:
172 file_handle.write(
173 '<!DOCTYPE html><html>'
174 '<head><title>rebaseline_server</title></head>'
175 '<body><ul>')
176 if SUMMARY_TYPES:
177 file_handle.write('<li>Expectations vs Actuals</li><ul>')
178 for summary_type in SUMMARY_TYPES:
179 file_handle.write(
180 '<li>'
181 '<a href="/%s/view.html#/view.html?resultsToLoad=/%s/%s">'
182 '%s</a></li>' % (
183 STATIC_CONTENTS_SUBDIR, RESULTS_SUBDIR,
184 summary_type, summary_type))
185 file_handle.write('</ul>')
186 if config_pairs:
187 file_handle.write('<li>Comparing configs within actual results</li><ul>')
188 for config_pair in config_pairs:
189 file_handle.write('<li>%s vs %s:' % config_pair)
190 for summary_type in SUMMARY_TYPES:
191 file_handle.write(
192 ' <a href="/%s/view.html#/view.html?'
193 'resultsToLoad=/%s/%s/%s-vs-%s_%s.json">%s</a>' % (
194 STATIC_CONTENTS_SUBDIR, STATIC_CONTENTS_SUBDIR,
195 GENERATED_JSON_SUBDIR, config_pair[0], config_pair[1],
196 summary_type, summary_type))
rmistry 2014/03/28 18:51:39 Instead of writing raw HTML what do you think abou
epoger 2014/03/28 19:25:55 Created a TODO.
197 file_handle.write('</li>')
198 file_handle.write('</ul>')
199 file_handle.write('</ul></body></html>')
200
201
140 class Server(object): 202 class Server(object):
141 """ HTTP server for our HTML rebaseline viewer. """ 203 """ HTTP server for our HTML rebaseline viewer. """
142 204
143 def __init__(self, 205 def __init__(self,
144 actuals_dir=DEFAULT_ACTUALS_DIR, 206 actuals_dir=DEFAULT_ACTUALS_DIR,
145 actuals_repo_revision=DEFAULT_ACTUALS_REPO_REVISION, 207 actuals_repo_revision=DEFAULT_ACTUALS_REPO_REVISION,
146 actuals_repo_url=DEFAULT_ACTUALS_REPO_URL, 208 actuals_repo_url=DEFAULT_ACTUALS_REPO_URL,
147 port=DEFAULT_PORT, export=False, editable=True, 209 port=DEFAULT_PORT, export=False, editable=True,
148 reload_seconds=0): 210 reload_seconds=0, config_pairs=None):
149 """ 211 """
150 Args: 212 Args:
151 actuals_dir: directory under which we will check out the latest actual 213 actuals_dir: directory under which we will check out the latest actual
152 GM results 214 GM results
153 actuals_repo_revision: revision of actual-results.json files to process 215 actuals_repo_revision: revision of actual-results.json files to process
154 actuals_repo_url: SVN repo to download actual-results.json files from 216 actuals_repo_url: SVN repo to download actual-results.json files from
155 port: which TCP port to listen on for HTTP requests 217 port: which TCP port to listen on for HTTP requests
156 export: whether to allow HTTP clients on other hosts to access this server 218 export: whether to allow HTTP clients on other hosts to access this server
157 editable: whether HTTP clients are allowed to submit new baselines 219 editable: whether HTTP clients are allowed to submit new baselines
158 reload_seconds: polling interval with which to check for new results; 220 reload_seconds: polling interval with which to check for new results;
159 if 0, don't check for new results at all 221 if 0, don't check for new results at all
222 config_pairs: List of (string, string) tuples; for each tuple, compare
223 actual results of these two configs. If None or empty,
224 don't compare configs at all.
160 """ 225 """
161 self._actuals_dir = actuals_dir 226 self._actuals_dir = actuals_dir
162 self._actuals_repo_revision = actuals_repo_revision 227 self._actuals_repo_revision = actuals_repo_revision
163 self._actuals_repo_url = actuals_repo_url 228 self._actuals_repo_url = actuals_repo_url
164 self._port = port 229 self._port = port
165 self._export = export 230 self._export = export
166 self._editable = editable 231 self._editable = editable
167 self._reload_seconds = reload_seconds 232 self._reload_seconds = reload_seconds
233 self._config_pairs = config_pairs or []
234
235 _create_index(
236 file_path=os.path.join(
237 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR,
238 "index.html"),
239 config_pairs=config_pairs)
168 self._actuals_repo = _create_svn_checkout( 240 self._actuals_repo = _create_svn_checkout(
169 dir_path=actuals_dir, repo_url=actuals_repo_url) 241 dir_path=actuals_dir, repo_url=actuals_repo_url)
170 242
171 # Reentrant lock that must be held whenever updating EITHER of: 243 # Reentrant lock that must be held whenever updating EITHER of:
172 # 1. self._results 244 # 1. self._results
173 # 2. the expected or actual results on local disk 245 # 2. the expected or actual results on local disk
174 self.results_rlock = threading.RLock() 246 self.results_rlock = threading.RLock()
175 # self._results will be filled in by calls to update_results() 247 # self._results will be filled in by calls to update_results()
176 self._results = None 248 self._results = None
177 249
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
230 # 302 #
231 # Because Skia uses depot_tools, we have to update using "gclient sync" 303 # Because Skia uses depot_tools, we have to update using "gclient sync"
232 # instead of raw git (or SVN) update. Happily, this will work whether 304 # instead of raw git (or SVN) update. Happily, this will work whether
233 # the checkout was created using git or SVN. 305 # the checkout was created using git or SVN.
234 if self._reload_seconds: 306 if self._reload_seconds:
235 logging.info( 307 logging.info(
236 'Updating expected GM results in %s by syncing Skia repo ...' % 308 'Updating expected GM results in %s by syncing Skia repo ...' %
237 compare_to_expectations.DEFAULT_EXPECTATIONS_DIR) 309 compare_to_expectations.DEFAULT_EXPECTATIONS_DIR)
238 _run_command(['gclient', 'sync'], TRUNK_DIRECTORY) 310 _run_command(['gclient', 'sync'], TRUNK_DIRECTORY)
239 311
240 self._results = compare_to_expectations.Results( 312 self._results = compare_to_expectations.ExpectationComparisons(
241 actuals_root=self._actuals_dir, 313 actuals_root=self._actuals_dir,
242 generated_images_root=os.path.join( 314 generated_images_root=os.path.join(
243 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, 315 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR,
244 GENERATED_IMAGES_SUBDIR), 316 GENERATED_IMAGES_SUBDIR),
245 diff_base_url=posixpath.join( 317 diff_base_url=posixpath.join(
246 os.pardir, STATIC_CONTENTS_SUBDIR, GENERATED_IMAGES_SUBDIR)) 318 os.pardir, STATIC_CONTENTS_SUBDIR, GENERATED_IMAGES_SUBDIR))
247 319
320 json_dir = os.path.join(
321 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_JSON_SUBDIR)
322 if not os.path.isdir(json_dir):
323 os.makedirs(json_dir)
324
325 for config_pair in self._config_pairs:
326 config_comparisons = compare_configs.ConfigComparisons(
327 configs=config_pair,
328 actuals_root=self._actuals_dir,
329 generated_images_root=os.path.join(
330 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR,
331 GENERATED_IMAGES_SUBDIR),
332 diff_base_url=posixpath.join(
333 os.pardir, GENERATED_IMAGES_SUBDIR))
334 for summary_type in SUMMARY_TYPES:
335 gm_json.WriteToFile(
336 config_comparisons.get_packaged_results_of_type(
337 results_type=summary_type),
338 os.path.join(
339 json_dir, '%s-vs-%s_%s.json' % (
340 config_pair[0], config_pair[1], summary_type)))
341
248 def _result_loader(self, reload_seconds=0): 342 def _result_loader(self, reload_seconds=0):
249 """ Call self.update_results(), either once or periodically. 343 """ Call self.update_results(), either once or periodically.
250 344
251 Params: 345 Params:
252 reload_seconds: integer; if nonzero, reload results at this interval 346 reload_seconds: integer; if nonzero, reload results at this interval
253 (in which case, this method will never return!) 347 (in which case, this method will never return!)
254 """ 348 """
255 self.update_results() 349 self.update_results()
256 logging.info('Initial results loaded. Ready for requests on %s' % self._url) 350 logging.info('Initial results loaded. Ready for requests on %s' % self._url)
257 if reload_seconds: 351 if reload_seconds:
(...skipping 29 matching lines...) Expand all
287 def do_GET(self): 381 def do_GET(self):
288 """ 382 """
289 Handles all GET requests, forwarding them to the appropriate 383 Handles all GET requests, forwarding them to the appropriate
290 do_GET_* dispatcher. 384 do_GET_* dispatcher.
291 385
292 If we see any Exceptions, return a 404. This fixes http://skbug.com/2147 386 If we see any Exceptions, return a 404. This fixes http://skbug.com/2147
293 """ 387 """
294 try: 388 try:
295 logging.debug('do_GET: path="%s"' % self.path) 389 logging.debug('do_GET: path="%s"' % self.path)
296 if self.path == '' or self.path == '/' or self.path == '/index.html' : 390 if self.path == '' or self.path == '/' or self.path == '/index.html' :
297 self.redirect_to('/%s/index.html' % STATIC_CONTENTS_SUBDIR) 391 self.redirect_to('/%s/%s/index.html' % (
392 STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR))
298 return 393 return
299 if self.path == '/favicon.ico' : 394 if self.path == '/favicon.ico' :
300 self.redirect_to('/%s/favicon.ico' % STATIC_CONTENTS_SUBDIR) 395 self.redirect_to('/%s/favicon.ico' % STATIC_CONTENTS_SUBDIR)
301 return 396 return
302 397
303 # All requests must be of this form: 398 # All requests must be of this form:
304 # /dispatcher/remainder 399 # /dispatcher/remainder
305 # where 'dispatcher' indicates which do_GET_* dispatcher to run 400 # where 'dispatcher' indicates which do_GET_* dispatcher to run
306 # and 'remainder' is the remaining path sent to that dispatcher. 401 # and 'remainder' is the remaining path sent to that dispatcher.
307 normpath = posixpath.normpath(self.path) 402 normpath = posixpath.normpath(self.path)
308 (dispatcher_name, remainder) = PATHSPLIT_RE.match(normpath).groups() 403 (dispatcher_name, remainder) = PATHSPLIT_RE.match(normpath).groups()
309 dispatchers = { 404 dispatchers = {
310 'results': self.do_GET_results, 405 RESULTS_SUBDIR: self.do_GET_results,
311 STATIC_CONTENTS_SUBDIR: self.do_GET_static, 406 STATIC_CONTENTS_SUBDIR: self.do_GET_static,
312 } 407 }
313 dispatcher = dispatchers[dispatcher_name] 408 dispatcher = dispatchers[dispatcher_name]
314 dispatcher(remainder) 409 dispatcher(remainder)
315 except: 410 except:
316 self.send_error(404) 411 self.send_error(404)
317 raise 412 raise
318 413
319 def do_GET_results(self, results_type): 414 def do_GET_results(self, results_type):
320 """ Handle a GET request for GM results. 415 """ Handle a GET request for GM results.
321 416
322 Args: 417 Args:
323 results_type: string indicating which set of results to return; 418 results_type: string indicating which set of results to return;
324 must be one of the results_mod.RESULTS_* constants 419 must be one of the results_mod.RESULTS_* constants
325 """ 420 """
326 logging.debug('do_GET_results: sending results of type "%s"' % results_type) 421 logging.debug('do_GET_results: sending results of type "%s"' % results_type)
327 # Since we must make multiple calls to the Results object, grab a 422 # Since we must make multiple calls to the ExpectationComparisons object,
328 # reference to it in case it is updated to point at a new Results 423 # grab a reference to it in case it is updated to point at a new
329 # object within another thread. 424 # ExpectationComparisons object within another thread.
330 # 425 #
331 # TODO(epoger): Rather than using a global variable for the handler 426 # TODO(epoger): Rather than using a global variable for the handler
332 # to refer to the Server object, make Server a subclass of 427 # to refer to the Server object, make Server a subclass of
333 # HTTPServer, and then it could be available to the handler via 428 # HTTPServer, and then it could be available to the handler via
334 # the handler's .server instance variable. 429 # the handler's .server instance variable.
335 results_obj = _SERVER.results 430 results_obj = _SERVER.results
336 if results_obj: 431 if results_obj:
337 response_dict = results_obj.get_packaged_results_of_type( 432 response_dict = results_obj.get_packaged_results_of_type(
338 results_type=results_type, reload_seconds=_SERVER.reload_seconds, 433 results_type=results_type, reload_seconds=_SERVER.reload_seconds,
339 is_editable=_SERVER.is_editable, is_exported=_SERVER.is_exported) 434 is_editable=_SERVER.is_editable, is_exported=_SERVER.is_exported)
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
509 parser.add_argument('--actuals-repo', 604 parser.add_argument('--actuals-repo',
510 help=('URL of SVN repo to download actual-results.json ' 605 help=('URL of SVN repo to download actual-results.json '
511 'files from. Defaults to %(default)s'), 606 'files from. Defaults to %(default)s'),
512 default=DEFAULT_ACTUALS_REPO_URL) 607 default=DEFAULT_ACTUALS_REPO_URL)
513 parser.add_argument('--actuals-revision', 608 parser.add_argument('--actuals-revision',
514 help=('revision of actual-results.json files to process. ' 609 help=('revision of actual-results.json files to process. '
515 'Defaults to %(default)s . Beware of setting this ' 610 'Defaults to %(default)s . Beware of setting this '
516 'argument in conjunction with --editable; you ' 611 'argument in conjunction with --editable; you '
517 'probably only want to edit results at HEAD.'), 612 'probably only want to edit results at HEAD.'),
518 default=DEFAULT_ACTUALS_REPO_REVISION) 613 default=DEFAULT_ACTUALS_REPO_REVISION)
614 parser.add_argument('--compare-configs', action='store_true',
615 help=('In addition to generating differences between '
616 'expectations and actuals, also generate '
617 'differences between these config pairs: '
618 + str(CONFIG_PAIRS_TO_COMPARE)))
519 parser.add_argument('--editable', action='store_true', 619 parser.add_argument('--editable', action='store_true',
520 help=('Allow HTTP clients to submit new baselines.')) 620 help=('Allow HTTP clients to submit new baselines.'))
521 parser.add_argument('--export', action='store_true', 621 parser.add_argument('--export', action='store_true',
522 help=('Instead of only allowing access from HTTP clients ' 622 help=('Instead of only allowing access from HTTP clients '
523 'on localhost, allow HTTP clients on other hosts ' 623 'on localhost, allow HTTP clients on other hosts '
524 'to access this server. WARNING: doing so will ' 624 'to access this server. WARNING: doing so will '
525 'allow users on other hosts to modify your ' 625 'allow users on other hosts to modify your '
526 'GM expectations, if combined with --editable.')) 626 'GM expectations, if combined with --editable.'))
527 parser.add_argument('--port', type=int, 627 parser.add_argument('--port', type=int,
528 help=('Which TCP port to listen on for HTTP requests; ' 628 help=('Which TCP port to listen on for HTTP requests; '
529 'defaults to %(default)s'), 629 'defaults to %(default)s'),
530 default=DEFAULT_PORT) 630 default=DEFAULT_PORT)
531 parser.add_argument('--reload', type=int, 631 parser.add_argument('--reload', type=int,
532 help=('How often (a period in seconds) to update the ' 632 help=('How often (a period in seconds) to update the '
533 'results. If specified, both expected and actual ' 633 'results. If specified, both expected and actual '
534 'results will be updated by running "gclient sync" ' 634 'results will be updated by running "gclient sync" '
535 'on your Skia checkout as a whole. ' 635 'on your Skia checkout as a whole. '
536 'By default, we do not reload at all, and you ' 636 'By default, we do not reload at all, and you '
537 'must restart the server to pick up new data.'), 637 'must restart the server to pick up new data.'),
538 default=0) 638 default=0)
539 args = parser.parse_args() 639 args = parser.parse_args()
640 if args.compare_configs:
641 config_pairs = CONFIG_PAIRS_TO_COMPARE
642 else:
643 config_pairs = None
644
540 global _SERVER 645 global _SERVER
541 _SERVER = Server(actuals_dir=args.actuals_dir, 646 _SERVER = Server(actuals_dir=args.actuals_dir,
542 actuals_repo_revision=args.actuals_revision, 647 actuals_repo_revision=args.actuals_revision,
543 actuals_repo_url=args.actuals_repo, 648 actuals_repo_url=args.actuals_repo,
544 port=args.port, export=args.export, editable=args.editable, 649 port=args.port, export=args.export, editable=args.editable,
545 reload_seconds=args.reload) 650 reload_seconds=args.reload, config_pairs=config_pairs)
546 _SERVER.run() 651 _SERVER.run()
547 652
548 653
549 if __name__ == '__main__': 654 if __name__ == '__main__':
550 main() 655 main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698