server.py revision defe6fdbc8edb2df0887c007450a8d8cc446f420
1f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com#!/usr/bin/python 2f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 39fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com""" 4f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comCopyright 2013 Google Inc. 5f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 6f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comUse of this source code is governed by a BSD-style license that can be 7f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comfound in the LICENSE file. 8f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 9f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comHTTP server for our HTML rebaseline viewer. 109fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com""" 11f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 12f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# System-level imports 13f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comimport argparse 14f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comimport BaseHTTPServer 15f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comimport json 16dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.comimport logging 17f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comimport os 18f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comimport posixpath 19f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comimport re 20f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comimport shutil 21b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.comimport socket 22d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.comimport subprocess 23f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comimport sys 24542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.comimport thread 25d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.comimport threading 26542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.comimport time 27dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.comimport urlparse 28f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 29f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# Imports from within Skia 30f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# 3131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# We need to add the 'tools' directory for svn.py, and the 'gm' directory for 3231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# gm_json.py . 33a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org# that directory. 34a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org# Make sure that the 'tools' dir is in the PYTHONPATH, but add it at the *end* 35f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# so any dirs that are already in the PYTHONPATH will be preferred. 36cb55f11a8f8ad66cdfc7643298a8772837cc35dfepoger@google.comPARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) 3731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgGM_DIRECTORY = os.path.dirname(PARENT_DIRECTORY) 3831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgTRUNK_DIRECTORY = os.path.dirname(GM_DIRECTORY) 39f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comTOOLS_DIRECTORY = os.path.join(TRUNK_DIRECTORY, 'tools') 40f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comif TOOLS_DIRECTORY not in sys.path: 41f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com sys.path.append(TOOLS_DIRECTORY) 42f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comimport svn 4331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgif GM_DIRECTORY not in sys.path: 4431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org sys.path.append(GM_DIRECTORY) 4531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgimport gm_json 46f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 47f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# Imports from local dir 487498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org# 497498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org# Note: we import results under a different name, to avoid confusion with the 507498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org# Server.results() property. See discussion at 517498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org# https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.py#newcode44 5231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgimport compare_configs 53b463d5668a498b672b80047f09901981afe513edcommit-bot@chromium.orgimport compare_to_expectations 5416f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgimport imagepairset 557498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.orgimport results as results_mod 56f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 57f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comPATHSPLIT_RE = re.compile('/([^/]+)/(.+)') 58f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 59f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# A simple dictionary of file name extensions to MIME types. The empty string 60f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# entry is used as the default when no extension was given or if the extension 61f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# has no entry in this dictionary. 62f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comMIME_TYPE_MAP = {'': 'application/octet-stream', 63f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'html': 'text/html', 64f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'css': 'text/css', 65f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'png': 'image/png', 66f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'js': 'application/javascript', 67f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'json': 'application/json' 68f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com } 69f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 7016f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org# Keys that server.py uses to create the toplevel content header. 7116f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org# NOTE: Keep these in sync with static/constants.js 7216f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__EDITS__MODIFICATIONS = 'modifications' 7316f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__EDITS__OLD_RESULTS_HASH = 'oldResultsHash' 7416f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' 757498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org 7631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgDEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR 775865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.orgDEFAULT_ACTUALS_REPO_REVISION = 'HEAD' 785865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.orgDEFAULT_ACTUALS_REPO_URL = 'http://skia-autogen.googlecode.com/svn/gm-actual' 79f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comDEFAULT_PORT = 8888 80f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 8131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# Directory, relative to PARENT_DIRECTORY, within which the server will serve 8231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# out live results (not static files). 8331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgRESULTS_SUBDIR = 'results' 8431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# Directory, relative to PARENT_DIRECTORY, within which the server will serve 8531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# out static files. 8631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgSTATIC_CONTENTS_SUBDIR = 'static' 8731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# All of the GENERATED_*_SUBDIRS are relative to STATIC_CONTENTS_SUBDIR 8831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgGENERATED_HTML_SUBDIR = 'generated-html' 8931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgGENERATED_IMAGES_SUBDIR = 'generated-images' 9031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgGENERATED_JSON_SUBDIR = 'generated-json' 91579942387bed9259b03419c593503022e1399931commit-bot@chromium.org 922682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com# How often (in seconds) clients should reload while waiting for initial 932682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com# results to load. 942682c90860655f6c25c61f97c8d8db309d03087aepoger@google.comRELOAD_INTERVAL_UNTIL_READY = 10 952682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com 9631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgSUMMARY_TYPES = [ 9731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org results_mod.KEY__HEADER__RESULTS_FAILURES, 9831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org results_mod.KEY__HEADER__RESULTS_ALL, 9931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org] 10031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# If --compare-configs is specified, compare these configs. 10131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgCONFIG_PAIRS_TO_COMPARE = [('8888', 'gpu')] 10231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 103eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com_HTTP_HEADER_CONTENT_LENGTH = 'Content-Length' 104eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com_HTTP_HEADER_CONTENT_TYPE = 'Content-Type' 105eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 106f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com_SERVER = None # This gets filled in by main() 107f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 108d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 109d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.comdef _run_command(args, directory): 110d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com """Runs a command and returns stdout as a single string. 111d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 112d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com Args: 113d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com args: the command to run, as a list of arguments 114d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com directory: directory within which to run the command 115d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 116d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com Returns: stdout, as a string 117d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 118d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com Raises an Exception if the command failed (exited with nonzero return code). 119d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com """ 120d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com logging.debug('_run_command: %s in directory %s' % (args, directory)) 121d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com proc = subprocess.Popen(args, cwd=directory, 122d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com stdout=subprocess.PIPE, 123d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com stderr=subprocess.PIPE) 124d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com (stdout, stderr) = proc.communicate() 125d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com if proc.returncode is not 0: 126d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com raise Exception('command "%s" failed in dir "%s": %s' % 127d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com (args, directory, stderr)) 128d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com return stdout 129d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 130d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 131591469b1e93f72172cef13a2f0675699994d7848epoger@google.comdef _get_routable_ip_address(): 132b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com """Returns routable IP address of this host (the IP address of its network 133b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com interface that would be used for most traffic, not its localhost 134b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com interface). See http://stackoverflow.com/a/166589 """ 135b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 136b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com sock.connect(('8.8.8.8', 80)) 137b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com host = sock.getsockname()[0] 138b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com sock.close() 139b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com return host 140b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com 141d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 142591469b1e93f72172cef13a2f0675699994d7848epoger@google.comdef _create_svn_checkout(dir_path, repo_url): 143591469b1e93f72172cef13a2f0675699994d7848epoger@google.com """Creates local checkout of an SVN repository at the specified directory 144591469b1e93f72172cef13a2f0675699994d7848epoger@google.com path, returning an svn.Svn object referring to the local checkout. 145591469b1e93f72172cef13a2f0675699994d7848epoger@google.com 146591469b1e93f72172cef13a2f0675699994d7848epoger@google.com Args: 147591469b1e93f72172cef13a2f0675699994d7848epoger@google.com dir_path: path to the local checkout; if this directory does not yet exist, 148591469b1e93f72172cef13a2f0675699994d7848epoger@google.com it will be created and the repo will be checked out into it 149591469b1e93f72172cef13a2f0675699994d7848epoger@google.com repo_url: URL of SVN repo to check out into dir_path (unless the local 150591469b1e93f72172cef13a2f0675699994d7848epoger@google.com checkout already exists) 151591469b1e93f72172cef13a2f0675699994d7848epoger@google.com Returns: an svn.Svn object referring to the local checkout. 152591469b1e93f72172cef13a2f0675699994d7848epoger@google.com """ 153591469b1e93f72172cef13a2f0675699994d7848epoger@google.com local_checkout = svn.Svn(dir_path) 154591469b1e93f72172cef13a2f0675699994d7848epoger@google.com if not os.path.isdir(dir_path): 155591469b1e93f72172cef13a2f0675699994d7848epoger@google.com os.makedirs(dir_path) 156591469b1e93f72172cef13a2f0675699994d7848epoger@google.com local_checkout.Checkout(repo_url, '.') 157591469b1e93f72172cef13a2f0675699994d7848epoger@google.com return local_checkout 158591469b1e93f72172cef13a2f0675699994d7848epoger@google.com 159b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com 16031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgdef _create_index(file_path, config_pairs): 16131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org """Creates an index file linking to all results available from this server. 16231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 16331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org Prior to https://codereview.chromium.org/215503002 , we had a static 16431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org index.html within our repo. But now that the results may or may not include 16531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config comparisons, index.html needs to be generated differently depending 16631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org on which results are included. 16731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 16831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org TODO(epoger): Instead of including raw HTML within the Python code, 16931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org consider restoring the index.html file as a template and using django (or 17031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org similar) to fill in dynamic content. 17131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 17231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org Args: 17331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_path: path on local disk to write index to; any directory components 17431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org of this path that do not already exist will be created 17531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_pairs: what pairs of configs (if any) we compare actual results of 17631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org """ 17731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org dir_path = os.path.dirname(file_path) 17831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org if not os.path.isdir(dir_path): 17931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org os.makedirs(dir_path) 18031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org with open(file_path, 'w') as file_handle: 18131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write( 18231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org '<!DOCTYPE html><html>' 18331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org '<head><title>rebaseline_server</title></head>' 18431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org '<body><ul>') 18531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org if SUMMARY_TYPES: 18631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write('<li>Expectations vs Actuals</li><ul>') 18731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org for summary_type in SUMMARY_TYPES: 18831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write( 18931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org '<li>' 19031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org '<a href="/%s/view.html#/view.html?resultsToLoad=/%s/%s">' 19131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org '%s</a></li>' % ( 19231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org STATIC_CONTENTS_SUBDIR, RESULTS_SUBDIR, 19331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org summary_type, summary_type)) 19431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write('</ul>') 19531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org if config_pairs: 19631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write('<li>Comparing configs within actual results</li><ul>') 19731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org for config_pair in config_pairs: 19831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write('<li>%s vs %s:' % config_pair) 19931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org for summary_type in SUMMARY_TYPES: 20031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write( 20131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org ' <a href="/%s/view.html#/view.html?' 20231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 'resultsToLoad=/%s/%s/%s-vs-%s_%s.json">%s</a>' % ( 20331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org STATIC_CONTENTS_SUBDIR, STATIC_CONTENTS_SUBDIR, 20431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org GENERATED_JSON_SUBDIR, config_pair[0], config_pair[1], 20531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org summary_type, summary_type)) 20631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write('</li>') 20731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write('</ul>') 20831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write('</ul></body></html>') 20931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 21031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 211f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comclass Server(object): 2129fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ HTTP server for our HTML rebaseline viewer. """ 2139fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com 214f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com def __init__(self, 215f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com actuals_dir=DEFAULT_ACTUALS_DIR, 2165865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org actuals_repo_revision=DEFAULT_ACTUALS_REPO_REVISION, 2175865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org actuals_repo_url=DEFAULT_ACTUALS_REPO_URL, 218542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com port=DEFAULT_PORT, export=False, editable=True, 219defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org reload_seconds=0, config_pairs=None, builder_regex_list=None): 2209fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ 2219fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com Args: 2229fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com actuals_dir: directory under which we will check out the latest actual 223c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org GM results 2245865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org actuals_repo_revision: revision of actual-results.json files to process 225c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org actuals_repo_url: SVN repo to download actual-results.json files from; 226c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org if None or '', don't fetch new actual-results files at all, 227c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org just compare to whatever files are already in actuals_dir 2289fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com port: which TCP port to listen on for HTTP requests 2299fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com export: whether to allow HTTP clients on other hosts to access this server 230542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com editable: whether HTTP clients are allowed to submit new baselines 231542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com reload_seconds: polling interval with which to check for new results; 232c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org if 0, don't check for new results at all 23331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_pairs: List of (string, string) tuples; for each tuple, compare 23431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org actual results of these two configs. If None or empty, 23531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org don't compare configs at all. 236defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org builder_regex_list: List of regular expressions specifying which builders 237defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org we will process. If None, process all builders. 2389fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ 239f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self._actuals_dir = actuals_dir 2405865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org self._actuals_repo_revision = actuals_repo_revision 2415865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org self._actuals_repo_url = actuals_repo_url 242f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self._port = port 243f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self._export = export 244542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com self._editable = editable 245542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com self._reload_seconds = reload_seconds 24631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org self._config_pairs = config_pairs or [] 247defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org self._builder_regex_list = builder_regex_list 24831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org _create_index( 24931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_path=os.path.join( 25031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR, 25131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org "index.html"), 25231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_pairs=config_pairs) 253c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org if actuals_repo_url: 254c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org self._actuals_repo = _create_svn_checkout( 255c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org dir_path=actuals_dir, repo_url=actuals_repo_url) 256591469b1e93f72172cef13a2f0675699994d7848epoger@google.com 257d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # Reentrant lock that must be held whenever updating EITHER of: 258d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # 1. self._results 259d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # 2. the expected or actual results on local disk 260d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com self.results_rlock = threading.RLock() 261d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # self._results will be filled in by calls to update_results() 262d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com self._results = None 263d7255b6ae43f1931d5ec086fe4e92a664e28ab6fepoger@google.com 264d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com @property 265d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com def results(self): 26650ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org """ Returns the most recently generated results, or None if we don't have 26750ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org any valid results (update_results() has not completed yet). """ 268d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com return self._results 269d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 270d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com @property 2719fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com def is_exported(self): 2729fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ Returns true iff HTTP clients on other hosts are allowed to access 2739fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com this server. """ 2749fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com return self._export 2759fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com 276d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com @property 277542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com def is_editable(self): 278542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com """ Returns true iff HTTP clients are allowed to submit new baselines. """ 279542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com return self._editable 280542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 281d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com @property 282542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com def reload_seconds(self): 283542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com """ Returns the result reload period in seconds, or 0 if we don't reload 284542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com results. """ 285542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com return self._reload_seconds 286f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 28750ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org def update_results(self, invalidate=False): 2887498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org """ Create or update self._results, based on the latest expectations and 2897498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org actuals. 290d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 291d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com We hold self.results_rlock while we do this, to guarantee that no other 292d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com thread attempts to update either self._results or the underlying files at 293d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com the same time. 29450ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org 29550ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org Args: 29650ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org invalidate: if True, invalidate self._results immediately upon entry; 29750ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org otherwise, we will let readers see those results until we 29850ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org replace them 299f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com """ 300d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com with self.results_rlock: 30150ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org if invalidate: 30250ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org self._results = None 303c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org if self._actuals_repo_url: 304c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org logging.info( 305c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org 'Updating actual GM results in %s to revision %s from repo %s ...' 306c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org % ( 307c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org self._actuals_dir, self._actuals_repo_revision, 308c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org self._actuals_repo_url)) 309c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org self._actuals_repo.Update( 310c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org path='.', revision=self._actuals_repo_revision) 311d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 312d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # We only update the expectations dir if the server was run with a 313d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # nonzero --reload argument; otherwise, we expect the user to maintain 314d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # her own expectations as she sees fit. 315d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # 316d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # Because the Skia repo is moving from SVN to git, and git does not 317d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # support updating a single directory tree, we have to update the entire 318d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # repo checkout. 319d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # 320d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # Because Skia uses depot_tools, we have to update using "gclient sync" 321d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # instead of raw git (or SVN) update. Happily, this will work whether 322d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # the checkout was created using git or SVN. 323d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com if self._reload_seconds: 324d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com logging.info( 325d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 'Updating expected GM results in %s by syncing Skia repo ...' % 326b463d5668a498b672b80047f09901981afe513edcommit-bot@chromium.org compare_to_expectations.DEFAULT_EXPECTATIONS_DIR) 327d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com _run_command(['gclient', 'sync'], TRUNK_DIRECTORY) 328591469b1e93f72172cef13a2f0675699994d7848epoger@google.com 32931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org self._results = compare_to_expectations.ExpectationComparisons( 330579942387bed9259b03419c593503022e1399931commit-bot@chromium.org actuals_root=self._actuals_dir, 331a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org generated_images_root=os.path.join( 332a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, 333a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org GENERATED_IMAGES_SUBDIR), 334a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org diff_base_url=posixpath.join( 335defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org os.pardir, STATIC_CONTENTS_SUBDIR, GENERATED_IMAGES_SUBDIR), 336defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org builder_regex_list=self._builder_regex_list) 337f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 33831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org json_dir = os.path.join( 33931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_JSON_SUBDIR) 34031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org if not os.path.isdir(json_dir): 34131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org os.makedirs(json_dir) 34231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 34331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org for config_pair in self._config_pairs: 34431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_comparisons = compare_configs.ConfigComparisons( 34531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org configs=config_pair, 34631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org actuals_root=self._actuals_dir, 34731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org generated_images_root=os.path.join( 34831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, 34931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org GENERATED_IMAGES_SUBDIR), 35031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org diff_base_url=posixpath.join( 351defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org os.pardir, GENERATED_IMAGES_SUBDIR), 352defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org builder_regex_list=self._builder_regex_list) 35331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org for summary_type in SUMMARY_TYPES: 35431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org gm_json.WriteToFile( 35531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_comparisons.get_packaged_results_of_type( 35631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org results_type=summary_type), 35731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org os.path.join( 35831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org json_dir, '%s-vs-%s_%s.json' % ( 35931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_pair[0], config_pair[1], summary_type))) 36031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 3612682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com def _result_loader(self, reload_seconds=0): 3622682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com """ Call self.update_results(), either once or periodically. 3632682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com 3642682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com Params: 3652682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com reload_seconds: integer; if nonzero, reload results at this interval 3662682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com (in which case, this method will never return!) 367542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com """ 3682682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com self.update_results() 3692682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com logging.info('Initial results loaded. Ready for requests on %s' % self._url) 3702682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com if reload_seconds: 3712682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com while True: 3722682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com time.sleep(reload_seconds) 3732682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com self.update_results() 374542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 375f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com def run(self): 3762682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com arg_tuple = (self._reload_seconds,) # start_new_thread needs a tuple, 3772682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com # even though it holds just one param 3782682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com thread.start_new_thread(self._result_loader, arg_tuple) 379542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 380f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com if self._export: 381f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com server_address = ('', self._port) 382591469b1e93f72172cef13a2f0675699994d7848epoger@google.com host = _get_routable_ip_address() 383542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com if self._editable: 384542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com logging.warning('Running with combination of "export" and "editable" ' 385542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 'flags. Users on other machines will ' 386542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 'be able to modify your GM expectations!') 387f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com else: 388b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com host = '127.0.0.1' 389b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com server_address = (host, self._port) 390f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com http_server = BaseHTTPServer.HTTPServer(server_address, HTTPRequestHandler) 3912682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com self._url = 'http://%s:%d' % (host, http_server.server_port) 3922682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com logging.info('Listening for requests on %s' % self._url) 393f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com http_server.serve_forever() 394f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 395f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 396f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comclass HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 397f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com """ HTTP request handlers for various types of queries this server knows 398f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com how to handle (static HTML and Javascript, expected/actual results, etc.) 399f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com """ 400f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com def do_GET(self): 401e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org """ 402e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org Handles all GET requests, forwarding them to the appropriate 403e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org do_GET_* dispatcher. 404f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 405e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org If we see any Exceptions, return a 404. This fixes http://skbug.com/2147 406e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org """ 407e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org try: 408e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org logging.debug('do_GET: path="%s"' % self.path) 409e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org if self.path == '' or self.path == '/' or self.path == '/index.html' : 41031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org self.redirect_to('/%s/%s/index.html' % ( 41131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR)) 412e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org return 413e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org if self.path == '/favicon.ico' : 414a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org self.redirect_to('/%s/favicon.ico' % STATIC_CONTENTS_SUBDIR) 415e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org return 416e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org 417e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org # All requests must be of this form: 418e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org # /dispatcher/remainder 419e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org # where 'dispatcher' indicates which do_GET_* dispatcher to run 420e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org # and 'remainder' is the remaining path sent to that dispatcher. 421e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org normpath = posixpath.normpath(self.path) 422e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org (dispatcher_name, remainder) = PATHSPLIT_RE.match(normpath).groups() 423e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org dispatchers = { 42431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org RESULTS_SUBDIR: self.do_GET_results, 42531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org STATIC_CONTENTS_SUBDIR: self.do_GET_static, 426e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org } 427e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org dispatcher = dispatchers[dispatcher_name] 428e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org dispatcher(remainder) 429e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org except: 430e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org self.send_error(404) 431e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org raise 432f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 433a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org def do_GET_results(self, results_type): 434a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org """ Handle a GET request for GM results. 435a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org 436a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org Args: 437a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_type: string indicating which set of results to return; 438a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org must be one of the results_mod.RESULTS_* constants 439a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org """ 440a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org logging.debug('do_GET_results: sending results of type "%s"' % results_type) 44131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org # Since we must make multiple calls to the ExpectationComparisons object, 44231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org # grab a reference to it in case it is updated to point at a new 44331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org # ExpectationComparisons object within another thread. 444a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org # 445a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org # TODO(epoger): Rather than using a global variable for the handler 446a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org # to refer to the Server object, make Server a subclass of 447a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org # HTTPServer, and then it could be available to the handler via 448a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org # the handler's .server instance variable. 449a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_obj = _SERVER.results 450a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org if results_obj: 451a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org response_dict = results_obj.get_packaged_results_of_type( 452a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_type=results_type, reload_seconds=_SERVER.reload_seconds, 453a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org is_editable=_SERVER.is_editable, is_exported=_SERVER.is_exported) 454a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org else: 455a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org now = int(time.time()) 456a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org response_dict = { 457a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_mod.KEY__HEADER: { 458a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_mod.KEY__HEADER__SCHEMA_VERSION: ( 459a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_mod.REBASELINE_SERVER_SCHEMA_VERSION_NUMBER), 460a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_mod.KEY__HEADER__IS_STILL_LOADING: True, 461a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_mod.KEY__HEADER__TIME_UPDATED: now, 462a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_mod.KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE: ( 463a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org now + RELOAD_INTERVAL_UNTIL_READY), 464a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org }, 465a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org } 466a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org self.send_json_dict(response_dict) 467a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org 468f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com def do_GET_static(self, path): 469a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org """ Handle a GET request for a file under STATIC_CONTENTS_SUBDIR . 470a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org Only allow serving of files within STATIC_CONTENTS_SUBDIR that is a 4719fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com filesystem sibling of this script. 4729fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com 4739fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com Args: 474a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org path: path to file (within STATIC_CONTENTS_SUBDIR) to retrieve 4759fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ 476dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.com # Strip arguments ('?resultsToLoad=all') from the path 477dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.com path = urlparse.urlparse(path).path 478dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.com 479dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.com logging.debug('do_GET_static: sending file "%s"' % path) 480a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org static_dir = os.path.realpath(os.path.join( 481a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR)) 482a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org full_path = os.path.realpath(os.path.join(static_dir, path)) 483a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org if full_path.startswith(static_dir): 484cb55f11a8f8ad66cdfc7643298a8772837cc35dfepoger@google.com self.send_file(full_path) 485cb55f11a8f8ad66cdfc7643298a8772837cc35dfepoger@google.com else: 486dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.com logging.error( 487dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.com 'Attempted do_GET_static() of path [%s] outside of static dir [%s]' 488a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org % (full_path, static_dir)) 489cb55f11a8f8ad66cdfc7643298a8772837cc35dfepoger@google.com self.send_error(404) 490f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 491eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com def do_POST(self): 492eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com """ Handles all POST requests, forwarding them to the appropriate 493eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com do_POST_* dispatcher. """ 494eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com # All requests must be of this form: 495eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com # /dispatcher 496eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com # where 'dispatcher' indicates which do_POST_* dispatcher to run. 497e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org logging.debug('do_POST: path="%s"' % self.path) 498eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com normpath = posixpath.normpath(self.path) 499eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com dispatchers = { 500eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com '/edits': self.do_POST_edits, 501eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com } 502eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com try: 503eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com dispatcher = dispatchers[normpath] 504eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com dispatcher() 505eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com self.send_response(200) 506eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com except: 507eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com self.send_error(404) 508eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com raise 509eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 510eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com def do_POST_edits(self): 511eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com """ Handle a POST request with modifications to GM expectations, in this 512eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com format: 513eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 514eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com { 51516f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org KEY__EDITS__OLD_RESULTS_TYPE: 'all', # type of results that the client 51616f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org # loaded and then made 51716f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org # modifications to 51816f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org KEY__EDITS__OLD_RESULTS_HASH: 39850913, # hash of results when the client 51916f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org # loaded them (ensures that the 52016f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org # client and server apply 52116f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org # modifications to the same base) 52216f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org KEY__EDITS__MODIFICATIONS: [ 523b463d5668a498b672b80047f09901981afe513edcommit-bot@chromium.org # as needed by compare_to_expectations.edit_expectations() 524eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com ... 525eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com ], 526eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com } 527eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 528eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com Raises an Exception if there were any problems. 529eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com """ 530d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com if not _SERVER.is_editable: 531eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com raise Exception('this server is not running in --editable mode') 532eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 533eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com content_type = self.headers[_HTTP_HEADER_CONTENT_TYPE] 534eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com if content_type != 'application/json;charset=UTF-8': 535eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com raise Exception('unsupported %s [%s]' % ( 536eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com _HTTP_HEADER_CONTENT_TYPE, content_type)) 537eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 538eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com content_length = int(self.headers[_HTTP_HEADER_CONTENT_LENGTH]) 539eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com json_data = self.rfile.read(content_length) 540eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com data = json.loads(json_data) 541eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com logging.debug('do_POST_edits: received new GM expectations data [%s]' % 542eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com data) 543eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 544d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # Update the results on disk with the information we received from the 545d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # client. 546d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # We must hold _SERVER.results_rlock while we do this, to guarantee that 547d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # no other thread updates expectations (from the Skia repo) while we are 548d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # updating them (using the info we received from the client). 549d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com with _SERVER.results_rlock: 55016f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org oldResultsType = data[KEY__EDITS__OLD_RESULTS_TYPE] 551d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com oldResults = _SERVER.results.get_results_of_type(oldResultsType) 55216f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org oldResultsHash = str(hash(repr(oldResults[imagepairset.KEY__IMAGEPAIRS]))) 55316f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org if oldResultsHash != data[KEY__EDITS__OLD_RESULTS_HASH]: 554d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com raise Exception('results of type "%s" changed while the client was ' 555d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 'making modifications. The client should reload the ' 556d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 'results and submit the modifications again.' % 557d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com oldResultsType) 55816f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org _SERVER.results.edit_expectations(data[KEY__EDITS__MODIFICATIONS]) 55950ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org 56050ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org # Read the updated results back from disk. 56150ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org # We can do this in a separate thread; we should return our success message 56250ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org # to the UI as soon as possible. 56350ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org thread.start_new_thread(_SERVER.update_results, (True,)) 564eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 565f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com def redirect_to(self, url): 5669fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ Redirect the HTTP client to a different url. 5679fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com 5689fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com Args: 5699fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com url: URL to redirect the HTTP client to 5709fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ 571f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.send_response(301) 572f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.send_header('Location', url) 573f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.end_headers() 574f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 575f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com def send_file(self, path): 576f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com """ Send the contents of the file at this path, with a mimetype based 5779fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com on the filename extension. 5789fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com 5799fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com Args: 5809fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com path: path of file whose contents to send to the HTTP client 5819fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ 582f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com # Grab the extension if there is one 583f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com extension = os.path.splitext(path)[1] 584f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com if len(extension) >= 1: 585f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com extension = extension[1:] 586f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 587f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com # Determine the MIME type of the file from its extension 588f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com mime_type = MIME_TYPE_MAP.get(extension, MIME_TYPE_MAP['']) 589f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 590f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com # Open the file and send it over HTTP 591f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com if os.path.isfile(path): 592f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com with open(path, 'rb') as sending_file: 593f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.send_response(200) 594f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.send_header('Content-type', mime_type) 595f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.end_headers() 596f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.wfile.write(sending_file.read()) 597f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com else: 598f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.send_error(404) 599f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 600a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org def send_json_dict(self, json_dict): 601a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org """ Send the contents of this dictionary in JSON format, with a JSON 602a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org mimetype. 603a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org 604a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org Args: 605a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org json_dict: dictionary to send 606a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org """ 607a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org self.send_response(200) 608a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org self.send_header('Content-type', 'application/json') 609a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org self.end_headers() 610a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org json.dump(json_dict, self.wfile) 611a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org 612f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 613f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comdef main(): 614a6ecbb8414b017fcb262e645fffc5a3129ecdf43commit-bot@chromium.org logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', 615a6ecbb8414b017fcb262e645fffc5a3129ecdf43commit-bot@chromium.org datefmt='%m/%d/%Y %H:%M:%S', 616a6ecbb8414b017fcb262e645fffc5a3129ecdf43commit-bot@chromium.org level=logging.INFO) 617f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com parser = argparse.ArgumentParser() 618f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com parser.add_argument('--actuals-dir', 619f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com help=('Directory into which we will check out the latest ' 620f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'actual GM results. If this directory does not ' 621f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'exist, it will be created. Defaults to %(default)s'), 622f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com default=DEFAULT_ACTUALS_DIR) 6235865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org parser.add_argument('--actuals-repo', 6245865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org help=('URL of SVN repo to download actual-results.json ' 625c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org 'files from. Defaults to %(default)s ; if set to ' 626c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org 'empty string, just compare to actual-results ' 627c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org 'already found in ACTUALS_DIR.'), 6285865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org default=DEFAULT_ACTUALS_REPO_URL) 6295865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org parser.add_argument('--actuals-revision', 6305865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org help=('revision of actual-results.json files to process. ' 6315865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org 'Defaults to %(default)s . Beware of setting this ' 6325865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org 'argument in conjunction with --editable; you ' 6335865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org 'probably only want to edit results at HEAD.'), 6345865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org default=DEFAULT_ACTUALS_REPO_REVISION) 635defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org parser.add_argument('--builders', metavar='BUILDER_REGEX', nargs='+', 636defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org help=('Only process builders matching these regular ' 637defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org 'expressions. If unspecified, process all ' 638defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org 'builders.')) 63931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org parser.add_argument('--compare-configs', action='store_true', 64031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org help=('In addition to generating differences between ' 64131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 'expectations and actuals, also generate ' 64231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 'differences between these config pairs: ' 64331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org + str(CONFIG_PAIRS_TO_COMPARE))) 644542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com parser.add_argument('--editable', action='store_true', 645eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com help=('Allow HTTP clients to submit new baselines.')) 646f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com parser.add_argument('--export', action='store_true', 647f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com help=('Instead of only allowing access from HTTP clients ' 648f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'on localhost, allow HTTP clients on other hosts ' 649f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'to access this server. WARNING: doing so will ' 650f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'allow users on other hosts to modify your ' 651542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 'GM expectations, if combined with --editable.')) 652afaad3dd7002feb6e69983ddc4e5148d7803baedepoger@google.com parser.add_argument('--port', type=int, 653afaad3dd7002feb6e69983ddc4e5148d7803baedepoger@google.com help=('Which TCP port to listen on for HTTP requests; ' 654afaad3dd7002feb6e69983ddc4e5148d7803baedepoger@google.com 'defaults to %(default)s'), 655afaad3dd7002feb6e69983ddc4e5148d7803baedepoger@google.com default=DEFAULT_PORT) 656542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com parser.add_argument('--reload', type=int, 657542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com help=('How often (a period in seconds) to update the ' 658b063e13428cef96abda9a80b273c92e2ed9c8287epoger@google.com 'results. If specified, both expected and actual ' 659d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 'results will be updated by running "gclient sync" ' 660d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 'on your Skia checkout as a whole. ' 661542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 'By default, we do not reload at all, and you ' 662542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 'must restart the server to pick up new data.'), 663542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com default=0) 664f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com args = parser.parse_args() 66531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org if args.compare_configs: 66631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_pairs = CONFIG_PAIRS_TO_COMPARE 66731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org else: 66831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_pairs = None 66931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 670f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com global _SERVER 671542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com _SERVER = Server(actuals_dir=args.actuals_dir, 6725865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org actuals_repo_revision=args.actuals_revision, 6735865ec5f3a367e0398ba6c322916d12a08c5002bcommit-bot@chromium.org actuals_repo_url=args.actuals_repo, 674542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com port=args.port, export=args.export, editable=args.editable, 675defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org reload_seconds=args.reload, config_pairs=config_pairs, 676defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org builder_regex_list=args.builders) 677f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com _SERVER.run() 678f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 679d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 680f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comif __name__ == '__main__': 681f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com main() 682