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 23542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.comimport thread 24d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.comimport threading 25542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.comimport time 26fb261109ec2c33e9236667faf09c13574146e627epogerimport urllib 27dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.comimport urlparse 28f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 29e5481eca7e66fd194c3b897fa3b8911cfe420958epoger# Must fix up PYTHONPATH before importing from within Skia 303b5c86c7a2db25f82a8415b6c79d1ac580fc64aestephanaimport rs_fixpypath # pylint: disable=W0611 31e5481eca7e66fd194c3b897fa3b8911cfe420958epoger 32f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# Imports from within Skia 33133931f4abdafa5bb0bdea3a02af1e5a70d5ac98epogerfrom py.utils import gs_utils 34fb261109ec2c33e9236667faf09c13574146e627epogerimport buildbot_globals 3531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgimport gm_json 36f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 37f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# Imports from local dir 387498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org# 39e5481eca7e66fd194c3b897fa3b8911cfe420958epoger# pylint: disable=C0301 407498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org# Note: we import results under a different name, to avoid confusion with the 417498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org# Server.results() property. See discussion at 427498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org# https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.py#newcode44 43e5481eca7e66fd194c3b897fa3b8911cfe420958epoger# pylint: enable=C0301 4431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgimport compare_configs 450b7127635d8245de7ac704080d722d06e47621d0epogerimport compare_rendered_pictures 46b463d5668a498b672b80047f09901981afe513edcommit-bot@chromium.orgimport compare_to_expectations 47b144271179aaf82cb1151e9dfd8e866747402594epogerimport download_actuals 486132b436d8723beaf06d1a8a0880f4f1535908a0epogerimport imagediffdb 4916f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgimport imagepairset 507498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.orgimport results as results_mod 512c4352bb9db02db70b829f96f397faa80fade220epogerimport writable_expectations as writable_expectations_mod 522c4352bb9db02db70b829f96f397faa80fade220epoger 53f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 54f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comPATHSPLIT_RE = re.compile('/([^/]+)/(.+)') 55f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 56f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# A simple dictionary of file name extensions to MIME types. The empty string 57f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# entry is used as the default when no extension was given or if the extension 58f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# has no entry in this dictionary. 59f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comMIME_TYPE_MAP = {'': 'application/octet-stream', 60f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'html': 'text/html', 61f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'css': 'text/css', 62f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'png': 'image/png', 63f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'js': 'application/javascript', 64f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'json': 'application/json' 65f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com } 66f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 6716f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org# Keys that server.py uses to create the toplevel content header. 6816f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org# NOTE: Keep these in sync with static/constants.js 6916f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__EDITS__MODIFICATIONS = 'modifications' 7016f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__EDITS__OLD_RESULTS_HASH = 'oldResultsHash' 7116f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' 722c4352bb9db02db70b829f96f397faa80fade220epogerKEY__LIVE_EDITS__MODIFICATIONS = 'modifications' 732c4352bb9db02db70b829f96f397faa80fade220epogerKEY__LIVE_EDITS__SET_A_DESCRIPTIONS = 'setA' 742c4352bb9db02db70b829f96f397faa80fade220epogerKEY__LIVE_EDITS__SET_B_DESCRIPTIONS = 'setB' 757498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org 7631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgDEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR 77b144271179aaf82cb1151e9dfd8e866747402594epogerDEFAULT_GM_SUMMARIES_BUCKET = download_actuals.GM_SUMMARIES_BUCKET 78b144271179aaf82cb1151e9dfd8e866747402594epogerDEFAULT_JSON_FILENAME = download_actuals.DEFAULT_JSON_FILENAME 79f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comDEFAULT_PORT = 8888 80f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 81b144271179aaf82cb1151e9dfd8e866747402594epogerPARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) 82b144271179aaf82cb1151e9dfd8e866747402594epogerTRUNK_DIRECTORY = os.path.dirname(os.path.dirname(PARENT_DIRECTORY)) 833facc7c87d9f81c352c9d37b1b46340b9e745578epoger 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 923facc7c87d9f81c352c9d37b1b46340b9e745578epoger# Directives associated with various HTTP GET requests. 933facc7c87d9f81c352c9d37b1b46340b9e745578epogerGET__LIVE_RESULTS = 'live-results' 943facc7c87d9f81c352c9d37b1b46340b9e745578epogerGET__PRECOMPUTED_RESULTS = 'results' 953facc7c87d9f81c352c9d37b1b46340b9e745578epogerGET__PREFETCH_RESULTS = 'prefetch' 963facc7c87d9f81c352c9d37b1b46340b9e745578epogerGET__STATIC_CONTENTS = 'static' 973facc7c87d9f81c352c9d37b1b46340b9e745578epoger 98e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger# Parameters we use within do_GET_live_results() and do_GET_prefetch_results() 99e73cd5ab0f693ae0c81f5f1ca76045363b6f999depogerLIVE_PARAM__DOWNLOAD_ONLY_DIFFERING = 'downloadOnlyDifferingImages' 1000b7127635d8245de7ac704080d722d06e47621d0epogerLIVE_PARAM__SET_A_DIR = 'setADir' 101e73cd5ab0f693ae0c81f5f1ca76045363b6f999depogerLIVE_PARAM__SET_A_SECTION = 'setASection' 1020b7127635d8245de7ac704080d722d06e47621d0epogerLIVE_PARAM__SET_B_DIR = 'setBDir' 103e73cd5ab0f693ae0c81f5f1ca76045363b6f999depogerLIVE_PARAM__SET_B_SECTION = 'setBSection' 1043facc7c87d9f81c352c9d37b1b46340b9e745578epoger 1052682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com# How often (in seconds) clients should reload while waiting for initial 1062682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com# results to load. 1072682c90860655f6c25c61f97c8d8db309d03087aepoger@google.comRELOAD_INTERVAL_UNTIL_READY = 10 1082682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com 109fb261109ec2c33e9236667faf09c13574146e627epoger_GM_SUMMARY_TYPES = [ 11031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org results_mod.KEY__HEADER__RESULTS_FAILURES, 11131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org results_mod.KEY__HEADER__RESULTS_ALL, 11231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org] 11331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# If --compare-configs is specified, compare these configs. 11431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgCONFIG_PAIRS_TO_COMPARE = [('8888', 'gpu')] 11531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 116fb261109ec2c33e9236667faf09c13574146e627epoger# SKP results that are available to compare. 117fb261109ec2c33e9236667faf09c13574146e627epoger# 118fb261109ec2c33e9236667faf09c13574146e627epoger# TODO(stephana): We don't actually want to maintain this list of platforms. 119fb261109ec2c33e9236667faf09c13574146e627epoger# We are just putting them in here for now, as "convenience" links for testing 120fb261109ec2c33e9236667faf09c13574146e627epoger# SKP diffs. 121fb261109ec2c33e9236667faf09c13574146e627epoger# Ultimately, we will depend on buildbot steps linking to their own diffs on 122fb261109ec2c33e9236667faf09c13574146e627epoger# the shared rebaseline_server instance. 123fb261109ec2c33e9236667faf09c13574146e627epoger_SKP_BASE_GS_URL = 'gs://' + buildbot_globals.Get('skp_summaries_bucket') 1245c4b137592a7937b4d02d72a976fa783f8d3675aepoger_SKP_BASE_REPO_URL = ( 1255c4b137592a7937b4d02d72a976fa783f8d3675aepoger compare_rendered_pictures.REPO_URL_PREFIX + posixpath.join( 1265c4b137592a7937b4d02d72a976fa783f8d3675aepoger 'expectations', 'skp')) 127fb261109ec2c33e9236667faf09c13574146e627epoger_SKP_PLATFORMS = [ 128fb261109ec2c33e9236667faf09c13574146e627epoger 'Test-Mac10.8-MacMini4.1-GeForce320M-x86_64-Debug', 129fb261109ec2c33e9236667faf09c13574146e627epoger 'Test-Ubuntu12-ShuttleA-GTX660-x86-Release', 130fb261109ec2c33e9236667faf09c13574146e627epoger] 131fb261109ec2c33e9236667faf09c13574146e627epoger 132eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com_HTTP_HEADER_CONTENT_LENGTH = 'Content-Length' 133eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com_HTTP_HEADER_CONTENT_TYPE = 'Content-Type' 134eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 135f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com_SERVER = None # This gets filled in by main() 136f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 137d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 138d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.comdef _run_command(args, directory): 139d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com """Runs a command and returns stdout as a single string. 140d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 141d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com Args: 142d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com args: the command to run, as a list of arguments 143d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com directory: directory within which to run the command 144d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 145d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com Returns: stdout, as a string 146d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 147d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com Raises an Exception if the command failed (exited with nonzero return code). 148d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com """ 149d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com logging.debug('_run_command: %s in directory %s' % (args, directory)) 150d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com proc = subprocess.Popen(args, cwd=directory, 151d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com stdout=subprocess.PIPE, 152d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com stderr=subprocess.PIPE) 153d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com (stdout, stderr) = proc.communicate() 154d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com if proc.returncode is not 0: 155d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com raise Exception('command "%s" failed in dir "%s": %s' % 156d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com (args, directory, stderr)) 157d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com return stdout 158d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 159d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 160591469b1e93f72172cef13a2f0675699994d7848epoger@google.comdef _get_routable_ip_address(): 161b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com """Returns routable IP address of this host (the IP address of its network 162b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com interface that would be used for most traffic, not its localhost 163b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com interface). See http://stackoverflow.com/a/166589 """ 164b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 165b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com sock.connect(('8.8.8.8', 80)) 166b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com host = sock.getsockname()[0] 167b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com sock.close() 168b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com return host 169b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com 170d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 17131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgdef _create_index(file_path, config_pairs): 17231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org """Creates an index file linking to all results available from this server. 17331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 17431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org Prior to https://codereview.chromium.org/215503002 , we had a static 17531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org index.html within our repo. But now that the results may or may not include 17631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config comparisons, index.html needs to be generated differently depending 17731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org on which results are included. 17831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 17931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org TODO(epoger): Instead of including raw HTML within the Python code, 18031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org consider restoring the index.html file as a template and using django (or 18131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org similar) to fill in dynamic content. 18231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 18331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org Args: 18431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_path: path on local disk to write index to; any directory components 18531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org of this path that do not already exist will be created 18631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_pairs: what pairs of configs (if any) we compare actual results of 18731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org """ 18831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org dir_path = os.path.dirname(file_path) 18931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org if not os.path.isdir(dir_path): 19031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org os.makedirs(dir_path) 19131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org with open(file_path, 'w') as file_handle: 19231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write( 19331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org '<!DOCTYPE html><html>' 19431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org '<head><title>rebaseline_server</title></head>' 19531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org '<body><ul>') 196fb261109ec2c33e9236667faf09c13574146e627epoger 197fb261109ec2c33e9236667faf09c13574146e627epoger if _GM_SUMMARY_TYPES: 198fb261109ec2c33e9236667faf09c13574146e627epoger file_handle.write('<li>GM Expectations vs Actuals</li><ul>') 199fb261109ec2c33e9236667faf09c13574146e627epoger for summary_type in _GM_SUMMARY_TYPES: 20031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write( 201fb261109ec2c33e9236667faf09c13574146e627epoger '\n<li><a href="/{static_directive}/view.html#/view.html?' 2023facc7c87d9f81c352c9d37b1b46340b9e745578epoger 'resultsToLoad=/{results_directive}/{summary_type}">' 203e5481eca7e66fd194c3b897fa3b8911cfe420958epoger '{summary_type}</a></li>'.format( 2043facc7c87d9f81c352c9d37b1b46340b9e745578epoger results_directive=GET__PRECOMPUTED_RESULTS, 2053facc7c87d9f81c352c9d37b1b46340b9e745578epoger static_directive=GET__STATIC_CONTENTS, 20646e51e10ae9a061456a5135a9973755f0b64547cepoger summary_type=summary_type)) 20731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write('</ul>') 208fb261109ec2c33e9236667faf09c13574146e627epoger 20931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org if config_pairs: 210fb261109ec2c33e9236667faf09c13574146e627epoger file_handle.write( 211fb261109ec2c33e9236667faf09c13574146e627epoger '\n<li>Comparing configs within actual GM results</li><ul>') 21231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org for config_pair in config_pairs: 21331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write('<li>%s vs %s:' % config_pair) 214fb261109ec2c33e9236667faf09c13574146e627epoger for summary_type in _GM_SUMMARY_TYPES: 21531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write( 21631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org ' <a href="/%s/view.html#/view.html?' 21731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 'resultsToLoad=/%s/%s/%s-vs-%s_%s.json">%s</a>' % ( 2183facc7c87d9f81c352c9d37b1b46340b9e745578epoger GET__STATIC_CONTENTS, GET__STATIC_CONTENTS, 21931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org GENERATED_JSON_SUBDIR, config_pair[0], config_pair[1], 22031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org summary_type, summary_type)) 22131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write('</li>') 22231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_handle.write('</ul>') 223fb261109ec2c33e9236667faf09c13574146e627epoger 224fb261109ec2c33e9236667faf09c13574146e627epoger if _SKP_PLATFORMS: 225fb261109ec2c33e9236667faf09c13574146e627epoger file_handle.write('\n<li>Rendered SKPs:<ul>') 226fb261109ec2c33e9236667faf09c13574146e627epoger for builder in _SKP_PLATFORMS: 227fb261109ec2c33e9236667faf09c13574146e627epoger file_handle.write( 228fb261109ec2c33e9236667faf09c13574146e627epoger '\n<li><a href="../live-view.html#live-view.html?%s">' % 229fb261109ec2c33e9236667faf09c13574146e627epoger urllib.urlencode({ 230fb261109ec2c33e9236667faf09c13574146e627epoger LIVE_PARAM__SET_A_SECTION: 231fb261109ec2c33e9236667faf09c13574146e627epoger gm_json.JSONKEY_EXPECTEDRESULTS, 232fb261109ec2c33e9236667faf09c13574146e627epoger LIVE_PARAM__SET_A_DIR: 2335c4b137592a7937b4d02d72a976fa783f8d3675aepoger posixpath.join(_SKP_BASE_REPO_URL, builder), 234fb261109ec2c33e9236667faf09c13574146e627epoger LIVE_PARAM__SET_B_SECTION: 235fb261109ec2c33e9236667faf09c13574146e627epoger gm_json.JSONKEY_ACTUALRESULTS, 236fb261109ec2c33e9236667faf09c13574146e627epoger LIVE_PARAM__SET_B_DIR: 237fb261109ec2c33e9236667faf09c13574146e627epoger posixpath.join(_SKP_BASE_GS_URL, builder), 238fb261109ec2c33e9236667faf09c13574146e627epoger })) 239fb261109ec2c33e9236667faf09c13574146e627epoger file_handle.write('expected vs actuals on %s</a></li>' % builder) 240fb261109ec2c33e9236667faf09c13574146e627epoger file_handle.write( 241fb261109ec2c33e9236667faf09c13574146e627epoger '\n<li><a href="../live-view.html#live-view.html?%s">' % 242fb261109ec2c33e9236667faf09c13574146e627epoger urllib.urlencode({ 243fb261109ec2c33e9236667faf09c13574146e627epoger LIVE_PARAM__SET_A_SECTION: 244fb261109ec2c33e9236667faf09c13574146e627epoger gm_json.JSONKEY_ACTUALRESULTS, 245fb261109ec2c33e9236667faf09c13574146e627epoger LIVE_PARAM__SET_A_DIR: 246fb261109ec2c33e9236667faf09c13574146e627epoger posixpath.join(_SKP_BASE_GS_URL, _SKP_PLATFORMS[0]), 247fb261109ec2c33e9236667faf09c13574146e627epoger LIVE_PARAM__SET_B_SECTION: 248fb261109ec2c33e9236667faf09c13574146e627epoger gm_json.JSONKEY_ACTUALRESULTS, 249fb261109ec2c33e9236667faf09c13574146e627epoger LIVE_PARAM__SET_B_DIR: 250fb261109ec2c33e9236667faf09c13574146e627epoger posixpath.join(_SKP_BASE_GS_URL, _SKP_PLATFORMS[1]), 251fb261109ec2c33e9236667faf09c13574146e627epoger })) 252fb261109ec2c33e9236667faf09c13574146e627epoger file_handle.write('actuals on %s vs %s</a></li>' % ( 253fb261109ec2c33e9236667faf09c13574146e627epoger _SKP_PLATFORMS[0], _SKP_PLATFORMS[1])) 254fb261109ec2c33e9236667faf09c13574146e627epoger file_handle.write('</li>') 255fb261109ec2c33e9236667faf09c13574146e627epoger 256fb261109ec2c33e9236667faf09c13574146e627epoger file_handle.write('\n</ul></body></html>') 25731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 25831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 259f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comclass Server(object): 2609fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ HTTP server for our HTML rebaseline viewer. """ 2619fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com 262f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com def __init__(self, 263f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com actuals_dir=DEFAULT_ACTUALS_DIR, 264b144271179aaf82cb1151e9dfd8e866747402594epoger json_filename=DEFAULT_JSON_FILENAME, 265b144271179aaf82cb1151e9dfd8e866747402594epoger gm_summaries_bucket=DEFAULT_GM_SUMMARIES_BUCKET, 266542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com port=DEFAULT_PORT, export=False, editable=True, 2670b7127635d8245de7ac704080d722d06e47621d0epoger reload_seconds=0, config_pairs=None, builder_regex_list=None, 2680b7127635d8245de7ac704080d722d06e47621d0epoger boto_file_path=None, 2690b7127635d8245de7ac704080d722d06e47621d0epoger imagediffdb_threads=imagediffdb.DEFAULT_NUM_WORKER_THREADS): 2709fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ 2719fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com Args: 2729fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com actuals_dir: directory under which we will check out the latest actual 273c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org GM results 274b144271179aaf82cb1151e9dfd8e866747402594epoger json_filename: basename of the JSON summary file to load for each builder 275b144271179aaf82cb1151e9dfd8e866747402594epoger gm_summaries_bucket: Google Storage bucket to download json_filename 276b144271179aaf82cb1151e9dfd8e866747402594epoger files from; if None or '', don't fetch new actual-results files 277b144271179aaf82cb1151e9dfd8e866747402594epoger at all, just compare to whatever files are already in actuals_dir 2789fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com port: which TCP port to listen on for HTTP requests 2799fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com export: whether to allow HTTP clients on other hosts to access this server 2804c808253f4f41f61309cf4449476e6bbbbd86631epoger editable: whether HTTP clients are allowed to submit new GM baselines 2814c808253f4f41f61309cf4449476e6bbbbd86631epoger (SKP baseline modifications are performed using an entirely different 2824c808253f4f41f61309cf4449476e6bbbbd86631epoger mechanism, not affected by this parameter) 283542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com reload_seconds: polling interval with which to check for new results; 284c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org if 0, don't check for new results at all 28531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_pairs: List of (string, string) tuples; for each tuple, compare 28631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org actual results of these two configs. If None or empty, 28731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org don't compare configs at all. 288defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org builder_regex_list: List of regular expressions specifying which builders 289defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org we will process. If None, process all builders. 2900b7127635d8245de7ac704080d722d06e47621d0epoger boto_file_path: Path to .boto file giving us credentials to access 2910b7127635d8245de7ac704080d722d06e47621d0epoger Google Storage buckets; if None, we will only be able to access 2920b7127635d8245de7ac704080d722d06e47621d0epoger public GS buckets. 2930b7127635d8245de7ac704080d722d06e47621d0epoger imagediffdb_threads: How many threads to spin up within imagediffdb. 2949fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ 295f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self._actuals_dir = actuals_dir 296b144271179aaf82cb1151e9dfd8e866747402594epoger self._json_filename = json_filename 297b144271179aaf82cb1151e9dfd8e866747402594epoger self._gm_summaries_bucket = gm_summaries_bucket 298f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self._port = port 299f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self._export = export 300542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com self._editable = editable 301542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com self._reload_seconds = reload_seconds 30231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org self._config_pairs = config_pairs or [] 303defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org self._builder_regex_list = builder_regex_list 3040b7127635d8245de7ac704080d722d06e47621d0epoger self.truncate_results = False 3050b7127635d8245de7ac704080d722d06e47621d0epoger 3060b7127635d8245de7ac704080d722d06e47621d0epoger if boto_file_path: 3070b7127635d8245de7ac704080d722d06e47621d0epoger self._gs = gs_utils.GSUtils(boto_file_path=boto_file_path) 3080b7127635d8245de7ac704080d722d06e47621d0epoger else: 3090b7127635d8245de7ac704080d722d06e47621d0epoger self._gs = gs_utils.GSUtils() 3100b7127635d8245de7ac704080d722d06e47621d0epoger 31131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org _create_index( 31231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org file_path=os.path.join( 31331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_HTML_SUBDIR, 31431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org "index.html"), 31531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_pairs=config_pairs) 316591469b1e93f72172cef13a2f0675699994d7848epoger@google.com 317d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # Reentrant lock that must be held whenever updating EITHER of: 318d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # 1. self._results 319d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # 2. the expected or actual results on local disk 320d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com self.results_rlock = threading.RLock() 3216132b436d8723beaf06d1a8a0880f4f1535908a0epoger 3220b7127635d8245de7ac704080d722d06e47621d0epoger # Create a single ImageDiffDB instance that is used by all our differs. 3230b7127635d8245de7ac704080d722d06e47621d0epoger self._image_diff_db = imagediffdb.ImageDiffDB( 3240b7127635d8245de7ac704080d722d06e47621d0epoger gs=self._gs, 3250b7127635d8245de7ac704080d722d06e47621d0epoger storage_root=os.path.join( 3260b7127635d8245de7ac704080d722d06e47621d0epoger PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, 3270b7127635d8245de7ac704080d722d06e47621d0epoger GENERATED_IMAGES_SUBDIR), 3280b7127635d8245de7ac704080d722d06e47621d0epoger num_worker_threads=imagediffdb_threads) 3290b7127635d8245de7ac704080d722d06e47621d0epoger 3300b7127635d8245de7ac704080d722d06e47621d0epoger # This will be filled in by calls to update_results() 331d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com self._results = None 332d7255b6ae43f1931d5ec086fe4e92a664e28ab6fepoger@google.com 333d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com @property 334d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com def results(self): 33550ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org """ Returns the most recently generated results, or None if we don't have 33650ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org any valid results (update_results() has not completed yet). """ 337d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com return self._results 338d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 339d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com @property 3400b7127635d8245de7ac704080d722d06e47621d0epoger def image_diff_db(self): 3410b7127635d8245de7ac704080d722d06e47621d0epoger """ Returns reference to our ImageDiffDB object.""" 3420b7127635d8245de7ac704080d722d06e47621d0epoger return self._image_diff_db 3430b7127635d8245de7ac704080d722d06e47621d0epoger 3440b7127635d8245de7ac704080d722d06e47621d0epoger @property 3450b7127635d8245de7ac704080d722d06e47621d0epoger def gs(self): 3460b7127635d8245de7ac704080d722d06e47621d0epoger """ Returns reference to our GSUtils object.""" 3470b7127635d8245de7ac704080d722d06e47621d0epoger return self._gs 3480b7127635d8245de7ac704080d722d06e47621d0epoger 3490b7127635d8245de7ac704080d722d06e47621d0epoger @property 3509fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com def is_exported(self): 3519fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ Returns true iff HTTP clients on other hosts are allowed to access 3529fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com this server. """ 3539fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com return self._export 3549fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com 355d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com @property 356542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com def is_editable(self): 3579f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger """ True iff HTTP clients are allowed to submit new GM baselines. 3589f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger 3599f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger TODO(epoger): This only pertains to GM baselines; SKP baselines are 3609f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger editable whenever expectations vs actuals are shown. 3619f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger Once we move the GM baselines to use the same code as the SKP baselines, 3629f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger we can delete this property. 3639f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger """ 364542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com return self._editable 365542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 366d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com @property 367542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com def reload_seconds(self): 368542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com """ Returns the result reload period in seconds, or 0 if we don't reload 369542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com results. """ 370542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com return self._reload_seconds 371f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 37250ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org def update_results(self, invalidate=False): 3737498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org """ Create or update self._results, based on the latest expectations and 3747498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.org actuals. 375d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 376d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com We hold self.results_rlock while we do this, to guarantee that no other 377d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com thread attempts to update either self._results or the underlying files at 378d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com the same time. 37950ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org 38050ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org Args: 38150ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org invalidate: if True, invalidate self._results immediately upon entry; 38250ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org otherwise, we will let readers see those results until we 38350ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org replace them 384f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com """ 385d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com with self.results_rlock: 38650ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org if invalidate: 38750ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org self._results = None 388b144271179aaf82cb1151e9dfd8e866747402594epoger if self._gm_summaries_bucket: 389c0df2fb5d0421a649d1dff9133874e440300fa7ccommit-bot@chromium.org logging.info( 390b144271179aaf82cb1151e9dfd8e866747402594epoger 'Updating GM result summaries in %s from gm_summaries_bucket %s ...' 391b144271179aaf82cb1151e9dfd8e866747402594epoger % (self._actuals_dir, self._gm_summaries_bucket)) 392b144271179aaf82cb1151e9dfd8e866747402594epoger 393b144271179aaf82cb1151e9dfd8e866747402594epoger # Clean out actuals_dir first, in case some builders have gone away 394b144271179aaf82cb1151e9dfd8e866747402594epoger # since we last ran. 395b144271179aaf82cb1151e9dfd8e866747402594epoger if os.path.isdir(self._actuals_dir): 396b144271179aaf82cb1151e9dfd8e866747402594epoger shutil.rmtree(self._actuals_dir) 397b144271179aaf82cb1151e9dfd8e866747402594epoger 398b144271179aaf82cb1151e9dfd8e866747402594epoger # Get the list of builders we care about. 399b144271179aaf82cb1151e9dfd8e866747402594epoger all_builders = download_actuals.get_builders_list( 400b144271179aaf82cb1151e9dfd8e866747402594epoger summaries_bucket=self._gm_summaries_bucket) 401b144271179aaf82cb1151e9dfd8e866747402594epoger if self._builder_regex_list: 402b144271179aaf82cb1151e9dfd8e866747402594epoger matching_builders = [] 403b144271179aaf82cb1151e9dfd8e866747402594epoger for builder in all_builders: 404b144271179aaf82cb1151e9dfd8e866747402594epoger for regex in self._builder_regex_list: 405b144271179aaf82cb1151e9dfd8e866747402594epoger if re.match(regex, builder): 406b144271179aaf82cb1151e9dfd8e866747402594epoger matching_builders.append(builder) 407b144271179aaf82cb1151e9dfd8e866747402594epoger break # go on to the next builder, no need to try more regexes 408b144271179aaf82cb1151e9dfd8e866747402594epoger else: 409b144271179aaf82cb1151e9dfd8e866747402594epoger matching_builders = all_builders 410b144271179aaf82cb1151e9dfd8e866747402594epoger 411b144271179aaf82cb1151e9dfd8e866747402594epoger # Download the JSON file for each builder we care about. 412b144271179aaf82cb1151e9dfd8e866747402594epoger # 413b144271179aaf82cb1151e9dfd8e866747402594epoger # TODO(epoger): When this is a large number of builders, we would be 414b144271179aaf82cb1151e9dfd8e866747402594epoger # better off downloading them in parallel! 415b144271179aaf82cb1151e9dfd8e866747402594epoger for builder in matching_builders: 41603f3db02c10d82ccbb4e3d3bd12fac689ce91fc6epoger self._gs.download_file( 417b144271179aaf82cb1151e9dfd8e866747402594epoger source_bucket=self._gm_summaries_bucket, 418b144271179aaf82cb1151e9dfd8e866747402594epoger source_path=posixpath.join(builder, self._json_filename), 419b144271179aaf82cb1151e9dfd8e866747402594epoger dest_path=os.path.join(self._actuals_dir, builder, 420b144271179aaf82cb1151e9dfd8e866747402594epoger self._json_filename), 421b144271179aaf82cb1151e9dfd8e866747402594epoger create_subdirs_if_needed=True) 422d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 423d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # We only update the expectations dir if the server was run with a 424d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # nonzero --reload argument; otherwise, we expect the user to maintain 425d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # her own expectations as she sees fit. 426d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # 427b144271179aaf82cb1151e9dfd8e866747402594epoger # Because the Skia repo is hosted using git, and git does not 428d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # support updating a single directory tree, we have to update the entire 429d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # repo checkout. 430d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # 431d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # Because Skia uses depot_tools, we have to update using "gclient sync" 432b144271179aaf82cb1151e9dfd8e866747402594epoger # instead of raw git commands. 433b144271179aaf82cb1151e9dfd8e866747402594epoger # 434b144271179aaf82cb1151e9dfd8e866747402594epoger # TODO(epoger): Fetch latest expectations in some other way. 435b144271179aaf82cb1151e9dfd8e866747402594epoger # Eric points out that our official documentation recommends an 436b144271179aaf82cb1151e9dfd8e866747402594epoger # unmanaged Skia checkout, so "gclient sync" will not bring down updated 437b144271179aaf82cb1151e9dfd8e866747402594epoger # expectations from origin/master-- you'd have to do a "git pull" of 438b144271179aaf82cb1151e9dfd8e866747402594epoger # some sort instead. 439b144271179aaf82cb1151e9dfd8e866747402594epoger # However, the live rebaseline_server at 440b144271179aaf82cb1151e9dfd8e866747402594epoger # http://skia-tree-status.appspot.com/redirect/rebaseline-server (which 441b144271179aaf82cb1151e9dfd8e866747402594epoger # is probably the only user of the --reload flag!) uses a managed 442b144271179aaf82cb1151e9dfd8e866747402594epoger # checkout, so "gclient sync" works in that case. 443b144271179aaf82cb1151e9dfd8e866747402594epoger # Probably the best idea is to avoid all of this nonsense by fetching 444b144271179aaf82cb1151e9dfd8e866747402594epoger # updated expectations into a temp directory, and leaving the rest of 445b144271179aaf82cb1151e9dfd8e866747402594epoger # the checkout alone. This could be done using "git show", or by 446b144271179aaf82cb1151e9dfd8e866747402594epoger # downloading individual expectation JSON files from 447b144271179aaf82cb1151e9dfd8e866747402594epoger # skia.googlesource.com . 448d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com if self._reload_seconds: 449d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com logging.info( 450d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 'Updating expected GM results in %s by syncing Skia repo ...' % 451b463d5668a498b672b80047f09901981afe513edcommit-bot@chromium.org compare_to_expectations.DEFAULT_EXPECTATIONS_DIR) 452d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com _run_command(['gclient', 'sync'], TRUNK_DIRECTORY) 453591469b1e93f72172cef13a2f0675699994d7848epoger@google.com 45431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org self._results = compare_to_expectations.ExpectationComparisons( 4556132b436d8723beaf06d1a8a0880f4f1535908a0epoger image_diff_db=self._image_diff_db, 456579942387bed9259b03419c593503022e1399931commit-bot@chromium.org actuals_root=self._actuals_dir, 457a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org diff_base_url=posixpath.join( 458defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org os.pardir, STATIC_CONTENTS_SUBDIR, GENERATED_IMAGES_SUBDIR), 459defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org builder_regex_list=self._builder_regex_list) 460f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 46131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org json_dir = os.path.join( 46231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_JSON_SUBDIR) 46331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org if not os.path.isdir(json_dir): 464e5481eca7e66fd194c3b897fa3b8911cfe420958epoger os.makedirs(json_dir) 46531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 46631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org for config_pair in self._config_pairs: 46731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_comparisons = compare_configs.ConfigComparisons( 46831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org configs=config_pair, 46931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org actuals_root=self._actuals_dir, 47031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org generated_images_root=os.path.join( 47131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, 47231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org GENERATED_IMAGES_SUBDIR), 47331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org diff_base_url=posixpath.join( 474defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org os.pardir, GENERATED_IMAGES_SUBDIR), 475defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org builder_regex_list=self._builder_regex_list) 476fb261109ec2c33e9236667faf09c13574146e627epoger for summary_type in _GM_SUMMARY_TYPES: 47731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org gm_json.WriteToFile( 47831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_comparisons.get_packaged_results_of_type( 47931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org results_type=summary_type), 48031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org os.path.join( 48131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org json_dir, '%s-vs-%s_%s.json' % ( 48231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_pair[0], config_pair[1], summary_type))) 48331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 4842682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com def _result_loader(self, reload_seconds=0): 4852682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com """ Call self.update_results(), either once or periodically. 4862682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com 4872682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com Params: 4882682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com reload_seconds: integer; if nonzero, reload results at this interval 4892682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com (in which case, this method will never return!) 490542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com """ 4912682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com self.update_results() 4922682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com logging.info('Initial results loaded. Ready for requests on %s' % self._url) 4932682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com if reload_seconds: 4942682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com while True: 4952682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com time.sleep(reload_seconds) 4962682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com self.update_results() 497542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 498f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com def run(self): 4992682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com arg_tuple = (self._reload_seconds,) # start_new_thread needs a tuple, 5002682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com # even though it holds just one param 5012682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com thread.start_new_thread(self._result_loader, arg_tuple) 502542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 503f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com if self._export: 504f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com server_address = ('', self._port) 505591469b1e93f72172cef13a2f0675699994d7848epoger@google.com host = _get_routable_ip_address() 506542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com if self._editable: 507542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com logging.warning('Running with combination of "export" and "editable" ' 508542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 'flags. Users on other machines will ' 509542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 'be able to modify your GM expectations!') 510f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com else: 511b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com host = '127.0.0.1' 512b08c707847be4b0c94adf592912b4e7073f71ecbepoger@google.com server_address = (host, self._port) 513e5481eca7e66fd194c3b897fa3b8911cfe420958epoger # pylint: disable=W0201 514f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com http_server = BaseHTTPServer.HTTPServer(server_address, HTTPRequestHandler) 5152682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com self._url = 'http://%s:%d' % (host, http_server.server_port) 5162682c90860655f6c25c61f97c8d8db309d03087aepoger@google.com logging.info('Listening for requests on %s' % self._url) 517f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com http_server.serve_forever() 518f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 519f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 520f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comclass HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 521f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com """ HTTP request handlers for various types of queries this server knows 522f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com how to handle (static HTML and Javascript, expected/actual results, etc.) 523f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com """ 524f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com def do_GET(self): 525e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org """ 526e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org Handles all GET requests, forwarding them to the appropriate 527e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org do_GET_* dispatcher. 528f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 529e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org If we see any Exceptions, return a 404. This fixes http://skbug.com/2147 530e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org """ 531e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org try: 532e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org logging.debug('do_GET: path="%s"' % self.path) 533e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org if self.path == '' or self.path == '/' or self.path == '/index.html' : 53431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org self.redirect_to('/%s/%s/index.html' % ( 5353facc7c87d9f81c352c9d37b1b46340b9e745578epoger GET__STATIC_CONTENTS, GENERATED_HTML_SUBDIR)) 536e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org return 537e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org if self.path == '/favicon.ico' : 5383facc7c87d9f81c352c9d37b1b46340b9e745578epoger self.redirect_to('/%s/favicon.ico' % GET__STATIC_CONTENTS) 539e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org return 540e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org 541e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org # All requests must be of this form: 542e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org # /dispatcher/remainder 543e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org # where 'dispatcher' indicates which do_GET_* dispatcher to run 544e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org # and 'remainder' is the remaining path sent to that dispatcher. 545c8d8a604bf995856d3f147dc3a1b010d8e4efa06epoger (dispatcher_name, remainder) = PATHSPLIT_RE.match(self.path).groups() 546e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org dispatchers = { 5473facc7c87d9f81c352c9d37b1b46340b9e745578epoger GET__LIVE_RESULTS: self.do_GET_live_results, 5483facc7c87d9f81c352c9d37b1b46340b9e745578epoger GET__PRECOMPUTED_RESULTS: self.do_GET_precomputed_results, 5493facc7c87d9f81c352c9d37b1b46340b9e745578epoger GET__PREFETCH_RESULTS: self.do_GET_prefetch_results, 5503facc7c87d9f81c352c9d37b1b46340b9e745578epoger GET__STATIC_CONTENTS: self.do_GET_static, 551e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org } 552e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org dispatcher = dispatchers[dispatcher_name] 553e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org dispatcher(remainder) 554e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org except: 555e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org self.send_error(404) 556e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org raise 557f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 5580b7127635d8245de7ac704080d722d06e47621d0epoger def do_GET_precomputed_results(self, results_type): 5590b7127635d8245de7ac704080d722d06e47621d0epoger """ Handle a GET request for part of the precomputed _SERVER.results object. 560a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org 561a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org Args: 562a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_type: string indicating which set of results to return; 563a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org must be one of the results_mod.RESULTS_* constants 564a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org """ 5650b7127635d8245de7ac704080d722d06e47621d0epoger logging.debug('do_GET_precomputed_results: sending results of type "%s"' % 5660b7127635d8245de7ac704080d722d06e47621d0epoger results_type) 56731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org # Since we must make multiple calls to the ExpectationComparisons object, 56831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org # grab a reference to it in case it is updated to point at a new 56931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org # ExpectationComparisons object within another thread. 570a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org # 571a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org # TODO(epoger): Rather than using a global variable for the handler 572a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org # to refer to the Server object, make Server a subclass of 573a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org # HTTPServer, and then it could be available to the handler via 574a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org # the handler's .server instance variable. 575a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_obj = _SERVER.results 576a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org if results_obj: 577a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org response_dict = results_obj.get_packaged_results_of_type( 578a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_type=results_type, reload_seconds=_SERVER.reload_seconds, 579a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org is_editable=_SERVER.is_editable, is_exported=_SERVER.is_exported) 580a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org else: 581a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org now = int(time.time()) 582a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org response_dict = { 58368a3815401f461976f76891d0477cb1440fa0abacommit-bot@chromium.org imagepairset.KEY__ROOT__HEADER: { 584a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_mod.KEY__HEADER__SCHEMA_VERSION: ( 58568a3815401f461976f76891d0477cb1440fa0abacommit-bot@chromium.org results_mod.VALUE__HEADER__SCHEMA_VERSION), 586a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_mod.KEY__HEADER__IS_STILL_LOADING: True, 587a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_mod.KEY__HEADER__TIME_UPDATED: now, 588a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org results_mod.KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE: ( 589a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org now + RELOAD_INTERVAL_UNTIL_READY), 590a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org }, 591a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org } 592a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org self.send_json_dict(response_dict) 593a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org 5944c808253f4f41f61309cf4449476e6bbbbd86631epoger def _get_live_results_or_prefetch(self, url_remainder, prefetch_only=False): 5954c808253f4f41f61309cf4449476e6bbbbd86631epoger """ Handle a GET request for live-generated image diff data. 5964c808253f4f41f61309cf4449476e6bbbbd86631epoger 5974c808253f4f41f61309cf4449476e6bbbbd86631epoger Args: 5984c808253f4f41f61309cf4449476e6bbbbd86631epoger url_remainder: string indicating which image diffs to generate 5994c808253f4f41f61309cf4449476e6bbbbd86631epoger prefetch_only: if True, the user isn't waiting around for results 6004c808253f4f41f61309cf4449476e6bbbbd86631epoger """ 6014c808253f4f41f61309cf4449476e6bbbbd86631epoger param_dict = urlparse.parse_qs(url_remainder) 6024c808253f4f41f61309cf4449476e6bbbbd86631epoger download_all_images = ( 6034c808253f4f41f61309cf4449476e6bbbbd86631epoger param_dict.get(LIVE_PARAM__DOWNLOAD_ONLY_DIFFERING, [''])[0].lower() 6044c808253f4f41f61309cf4449476e6bbbbd86631epoger not in ['1', 'true']) 605caa80b9a46ea00a7b582d96d73302939aa21b5eeepoger setA_dir = param_dict[LIVE_PARAM__SET_A_DIR][0] 606caa80b9a46ea00a7b582d96d73302939aa21b5eeepoger setB_dir = param_dict[LIVE_PARAM__SET_B_DIR][0] 6074c808253f4f41f61309cf4449476e6bbbbd86631epoger setA_section = self._validate_summary_section( 6084c808253f4f41f61309cf4449476e6bbbbd86631epoger param_dict.get(LIVE_PARAM__SET_A_SECTION, [None])[0]) 6094c808253f4f41f61309cf4449476e6bbbbd86631epoger setB_section = self._validate_summary_section( 6104c808253f4f41f61309cf4449476e6bbbbd86631epoger param_dict.get(LIVE_PARAM__SET_B_SECTION, [None])[0]) 6119f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger 6129f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger # If the sets show expectations vs actuals, always show expectations on 6139f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger # the left (setA). 6149f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger if ((setA_section == gm_json.JSONKEY_ACTUALRESULTS) and 6159f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger (setB_section == gm_json.JSONKEY_EXPECTEDRESULTS)): 616caa80b9a46ea00a7b582d96d73302939aa21b5eeepoger setA_dir, setB_dir = setB_dir, setA_dir 6179f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger setA_section, setB_section = setB_section, setA_section 6189f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger 6199f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger # Are we comparing some actuals against expectations stored in the repo? 6209f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger # If so, we can allow the user to submit new baselines. 6219f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger is_editable = ( 6229f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger (setA_section == gm_json.JSONKEY_EXPECTEDRESULTS) and 623caa80b9a46ea00a7b582d96d73302939aa21b5eeepoger (setA_dir.startswith(compare_rendered_pictures.REPO_URL_PREFIX)) and 6249f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger (setB_section == gm_json.JSONKEY_ACTUALRESULTS)) 6259f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger 6264c808253f4f41f61309cf4449476e6bbbbd86631epoger results_obj = compare_rendered_pictures.RenderedPicturesComparisons( 627caa80b9a46ea00a7b582d96d73302939aa21b5eeepoger setA_dir=setA_dir, setB_dir=setB_dir, 6284c808253f4f41f61309cf4449476e6bbbbd86631epoger setA_section=setA_section, setB_section=setB_section, 6294c808253f4f41f61309cf4449476e6bbbbd86631epoger image_diff_db=_SERVER.image_diff_db, 6304c808253f4f41f61309cf4449476e6bbbbd86631epoger diff_base_url='/static/generated-images', 6314c808253f4f41f61309cf4449476e6bbbbd86631epoger gs=_SERVER.gs, truncate_results=_SERVER.truncate_results, 6324c808253f4f41f61309cf4449476e6bbbbd86631epoger prefetch_only=prefetch_only, download_all_images=download_all_images) 6334c808253f4f41f61309cf4449476e6bbbbd86631epoger if prefetch_only: 6344c808253f4f41f61309cf4449476e6bbbbd86631epoger self.send_response(200) 6354c808253f4f41f61309cf4449476e6bbbbd86631epoger else: 6364c808253f4f41f61309cf4449476e6bbbbd86631epoger self.send_json_dict(results_obj.get_packaged_results_of_type( 6379f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger results_type=results_mod.KEY__HEADER__RESULTS_ALL, 6389f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger is_editable=is_editable)) 6394c808253f4f41f61309cf4449476e6bbbbd86631epoger 6400b7127635d8245de7ac704080d722d06e47621d0epoger def do_GET_live_results(self, url_remainder): 6410b7127635d8245de7ac704080d722d06e47621d0epoger """ Handle a GET request for live-generated image diff data. 6420b7127635d8245de7ac704080d722d06e47621d0epoger 6430b7127635d8245de7ac704080d722d06e47621d0epoger Args: 6440b7127635d8245de7ac704080d722d06e47621d0epoger url_remainder: string indicating which image diffs to generate 6450b7127635d8245de7ac704080d722d06e47621d0epoger """ 6460b7127635d8245de7ac704080d722d06e47621d0epoger logging.debug('do_GET_live_results: url_remainder="%s"' % url_remainder) 6474c808253f4f41f61309cf4449476e6bbbbd86631epoger self._get_live_results_or_prefetch( 6484c808253f4f41f61309cf4449476e6bbbbd86631epoger url_remainder=url_remainder, prefetch_only=False) 6490b7127635d8245de7ac704080d722d06e47621d0epoger 6503facc7c87d9f81c352c9d37b1b46340b9e745578epoger def do_GET_prefetch_results(self, url_remainder): 6513facc7c87d9f81c352c9d37b1b46340b9e745578epoger """ Prefetch image diff data for a future do_GET_live_results() call. 6523facc7c87d9f81c352c9d37b1b46340b9e745578epoger 6533facc7c87d9f81c352c9d37b1b46340b9e745578epoger Args: 6543facc7c87d9f81c352c9d37b1b46340b9e745578epoger url_remainder: string indicating which image diffs to generate 6553facc7c87d9f81c352c9d37b1b46340b9e745578epoger """ 6563facc7c87d9f81c352c9d37b1b46340b9e745578epoger logging.debug('do_GET_prefetch_results: url_remainder="%s"' % url_remainder) 6574c808253f4f41f61309cf4449476e6bbbbd86631epoger self._get_live_results_or_prefetch( 6584c808253f4f41f61309cf4449476e6bbbbd86631epoger url_remainder=url_remainder, prefetch_only=True) 6593facc7c87d9f81c352c9d37b1b46340b9e745578epoger 660f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com def do_GET_static(self, path): 661a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org """ Handle a GET request for a file under STATIC_CONTENTS_SUBDIR . 662a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org Only allow serving of files within STATIC_CONTENTS_SUBDIR that is a 6639fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com filesystem sibling of this script. 6649fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com 6659fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com Args: 666a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org path: path to file (within STATIC_CONTENTS_SUBDIR) to retrieve 6679fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ 668dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.com # Strip arguments ('?resultsToLoad=all') from the path 669dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.com path = urlparse.urlparse(path).path 670dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.com 671dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.com logging.debug('do_GET_static: sending file "%s"' % path) 672a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org static_dir = os.path.realpath(os.path.join( 673a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR)) 674a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org full_path = os.path.realpath(os.path.join(static_dir, path)) 675a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org if full_path.startswith(static_dir): 676cb55f11a8f8ad66cdfc7643298a8772837cc35dfepoger@google.com self.send_file(full_path) 677cb55f11a8f8ad66cdfc7643298a8772837cc35dfepoger@google.com else: 678dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.com logging.error( 679dcb4e65998913bfb2cc7e331ffacf0965bdee0eaepoger@google.com 'Attempted do_GET_static() of path [%s] outside of static dir [%s]' 680a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org % (full_path, static_dir)) 681cb55f11a8f8ad66cdfc7643298a8772837cc35dfepoger@google.com self.send_error(404) 682f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 683eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com def do_POST(self): 684eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com """ Handles all POST requests, forwarding them to the appropriate 685eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com do_POST_* dispatcher. """ 686eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com # All requests must be of this form: 687eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com # /dispatcher 688eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com # where 'dispatcher' indicates which do_POST_* dispatcher to run. 689e6af4fb34bd780ff24e0e967a8dc73639ccc2a9dcommit-bot@chromium.org logging.debug('do_POST: path="%s"' % self.path) 690eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com normpath = posixpath.normpath(self.path) 691eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com dispatchers = { 692eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com '/edits': self.do_POST_edits, 6932c4352bb9db02db70b829f96f397faa80fade220epoger '/live-edits': self.do_POST_live_edits, 694eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com } 695eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com try: 696eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com dispatcher = dispatchers[normpath] 697eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com dispatcher() 698eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com except: 699eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com self.send_error(404) 700eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com raise 701eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 702eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com def do_POST_edits(self): 703eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com """ Handle a POST request with modifications to GM expectations, in this 704eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com format: 705eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 706eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com { 70716f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org KEY__EDITS__OLD_RESULTS_TYPE: 'all', # type of results that the client 70816f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org # loaded and then made 70916f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org # modifications to 71016f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org KEY__EDITS__OLD_RESULTS_HASH: 39850913, # hash of results when the client 71116f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org # loaded them (ensures that the 71216f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org # client and server apply 71316f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org # modifications to the same base) 71416f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org KEY__EDITS__MODIFICATIONS: [ 715b463d5668a498b672b80047f09901981afe513edcommit-bot@chromium.org # as needed by compare_to_expectations.edit_expectations() 716eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com ... 717eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com ], 718eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com } 719eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 720eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com Raises an Exception if there were any problems. 721eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com """ 722d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com if not _SERVER.is_editable: 723eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com raise Exception('this server is not running in --editable mode') 724eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 725eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com content_type = self.headers[_HTTP_HEADER_CONTENT_TYPE] 726eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com if content_type != 'application/json;charset=UTF-8': 727eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com raise Exception('unsupported %s [%s]' % ( 728eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com _HTTP_HEADER_CONTENT_TYPE, content_type)) 729eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 730eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com content_length = int(self.headers[_HTTP_HEADER_CONTENT_LENGTH]) 731eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com json_data = self.rfile.read(content_length) 732eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com data = json.loads(json_data) 733eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com logging.debug('do_POST_edits: received new GM expectations data [%s]' % 734eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com data) 735eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 736d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # Update the results on disk with the information we received from the 737d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # client. 738d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # We must hold _SERVER.results_rlock while we do this, to guarantee that 739d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # no other thread updates expectations (from the Skia repo) while we are 740d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com # updating them (using the info we received from the client). 741d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com with _SERVER.results_rlock: 74216f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org oldResultsType = data[KEY__EDITS__OLD_RESULTS_TYPE] 743d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com oldResults = _SERVER.results.get_results_of_type(oldResultsType) 74468a3815401f461976f76891d0477cb1440fa0abacommit-bot@chromium.org oldResultsHash = str(hash(repr( 74568a3815401f461976f76891d0477cb1440fa0abacommit-bot@chromium.org oldResults[imagepairset.KEY__ROOT__IMAGEPAIRS]))) 74616f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org if oldResultsHash != data[KEY__EDITS__OLD_RESULTS_HASH]: 747d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com raise Exception('results of type "%s" changed while the client was ' 748d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 'making modifications. The client should reload the ' 749d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 'results and submit the modifications again.' % 750d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com oldResultsType) 75116f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org _SERVER.results.edit_expectations(data[KEY__EDITS__MODIFICATIONS]) 75250ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org 75350ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org # Read the updated results back from disk. 75450ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org # We can do this in a separate thread; we should return our success message 75550ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org # to the UI as soon as possible. 75650ad8e4d8efcd04f8a2c34cc32f6fecb3985a1a4commit-bot@chromium.org thread.start_new_thread(_SERVER.update_results, (True,)) 7572c4352bb9db02db70b829f96f397faa80fade220epoger self.send_response(200) 7582c4352bb9db02db70b829f96f397faa80fade220epoger 7592c4352bb9db02db70b829f96f397faa80fade220epoger def do_POST_live_edits(self): 7602c4352bb9db02db70b829f96f397faa80fade220epoger """ Handle a POST request with modifications to SKP expectations, in this 7612c4352bb9db02db70b829f96f397faa80fade220epoger format: 7622c4352bb9db02db70b829f96f397faa80fade220epoger 7632c4352bb9db02db70b829f96f397faa80fade220epoger { 7642c4352bb9db02db70b829f96f397faa80fade220epoger KEY__LIVE_EDITS__SET_A_DESCRIPTIONS: { 7652c4352bb9db02db70b829f96f397faa80fade220epoger # setA descriptions from the original data 7662c4352bb9db02db70b829f96f397faa80fade220epoger }, 7672c4352bb9db02db70b829f96f397faa80fade220epoger KEY__LIVE_EDITS__SET_B_DESCRIPTIONS: { 7682c4352bb9db02db70b829f96f397faa80fade220epoger # setB descriptions from the original data 7692c4352bb9db02db70b829f96f397faa80fade220epoger }, 7702c4352bb9db02db70b829f96f397faa80fade220epoger KEY__LIVE_EDITS__MODIFICATIONS: [ 7712c4352bb9db02db70b829f96f397faa80fade220epoger # as needed by writable_expectations.modify() 7722c4352bb9db02db70b829f96f397faa80fade220epoger ], 7732c4352bb9db02db70b829f96f397faa80fade220epoger } 7742c4352bb9db02db70b829f96f397faa80fade220epoger 7752c4352bb9db02db70b829f96f397faa80fade220epoger Raises an Exception if there were any problems. 7762c4352bb9db02db70b829f96f397faa80fade220epoger """ 7772c4352bb9db02db70b829f96f397faa80fade220epoger content_type = self.headers[_HTTP_HEADER_CONTENT_TYPE] 7782c4352bb9db02db70b829f96f397faa80fade220epoger if content_type != 'application/json;charset=UTF-8': 7792c4352bb9db02db70b829f96f397faa80fade220epoger raise Exception('unsupported %s [%s]' % ( 7802c4352bb9db02db70b829f96f397faa80fade220epoger _HTTP_HEADER_CONTENT_TYPE, content_type)) 7812c4352bb9db02db70b829f96f397faa80fade220epoger 7822c4352bb9db02db70b829f96f397faa80fade220epoger content_length = int(self.headers[_HTTP_HEADER_CONTENT_LENGTH]) 7832c4352bb9db02db70b829f96f397faa80fade220epoger json_data = self.rfile.read(content_length) 7842c4352bb9db02db70b829f96f397faa80fade220epoger data = json.loads(json_data) 7852c4352bb9db02db70b829f96f397faa80fade220epoger logging.debug('do_POST_live_edits: received new GM expectations data [%s]' % 7862c4352bb9db02db70b829f96f397faa80fade220epoger data) 7872c4352bb9db02db70b829f96f397faa80fade220epoger with writable_expectations_mod.WritableExpectations( 7882c4352bb9db02db70b829f96f397faa80fade220epoger data[KEY__LIVE_EDITS__SET_A_DESCRIPTIONS]) as writable_expectations: 7892c4352bb9db02db70b829f96f397faa80fade220epoger writable_expectations.modify(data[KEY__LIVE_EDITS__MODIFICATIONS]) 7902c4352bb9db02db70b829f96f397faa80fade220epoger diffs = writable_expectations.get_diffs() 7912c4352bb9db02db70b829f96f397faa80fade220epoger # TODO(stephana): Move to a simpler web framework so we don't have to 7922c4352bb9db02db70b829f96f397faa80fade220epoger # call these functions. See http://skbug.com/2856 ('rebaseline_server: 7932c4352bb9db02db70b829f96f397faa80fade220epoger # Refactor server to use a simple web framework') 7942c4352bb9db02db70b829f96f397faa80fade220epoger self.send_response(200) 7952c4352bb9db02db70b829f96f397faa80fade220epoger self.send_header('Content-type', 'text/plain') 7962c4352bb9db02db70b829f96f397faa80fade220epoger self.end_headers() 7972c4352bb9db02db70b829f96f397faa80fade220epoger self.wfile.write(diffs) 798eb832599b631a09b180ea3615347adba6bd5e363epoger@google.com 799f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com def redirect_to(self, url): 8009fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ Redirect the HTTP client to a different url. 8019fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com 8029fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com Args: 8039fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com url: URL to redirect the HTTP client to 8049fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ 805f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.send_response(301) 806f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.send_header('Location', url) 807f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.end_headers() 808f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 809f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com def send_file(self, path): 810f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com """ Send the contents of the file at this path, with a mimetype based 8119fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com on the filename extension. 8129fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com 8139fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com Args: 8149fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com path: path of file whose contents to send to the HTTP client 8159fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com """ 816f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com # Grab the extension if there is one 817f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com extension = os.path.splitext(path)[1] 818f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com if len(extension) >= 1: 819f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com extension = extension[1:] 820f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 821f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com # Determine the MIME type of the file from its extension 822f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com mime_type = MIME_TYPE_MAP.get(extension, MIME_TYPE_MAP['']) 823f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 824f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com # Open the file and send it over HTTP 825f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com if os.path.isfile(path): 826f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com with open(path, 'rb') as sending_file: 827f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.send_response(200) 828f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.send_header('Content-type', mime_type) 829f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.end_headers() 830f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.wfile.write(sending_file.read()) 831f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com else: 832f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com self.send_error(404) 833f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 834a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org def send_json_dict(self, json_dict): 835a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org """ Send the contents of this dictionary in JSON format, with a JSON 836a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org mimetype. 837a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org 838a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org Args: 839a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org json_dict: dictionary to send 840a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org """ 841a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org self.send_response(200) 842a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org self.send_header('Content-type', 'application/json') 843a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org self.end_headers() 844a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org json.dump(json_dict, self.wfile) 845a25c4e4fefd10980e2a4701607e494513a6c60b5commit-bot@chromium.org 846e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger def _validate_summary_section(self, section_name): 847e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger """Validates the section we have been requested to read within JSON summary. 848e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger 849e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger Args: 850e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger section_name: which section of the JSON summary file has been requested 851e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger 852e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger Returns: the validated section name 853e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger 854e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger Raises: Exception if an invalid section_name was requested. 855e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger """ 856e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger if section_name not in compare_rendered_pictures.ALLOWED_SECTION_NAMES: 857e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger raise Exception('requested section name "%s" not in allowed list %s' % ( 858e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger section_name, compare_rendered_pictures.ALLOWED_SECTION_NAMES)) 859e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger return section_name 860e73cd5ab0f693ae0c81f5f1ca76045363b6f999depoger 861f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 862f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comdef main(): 863a6ecbb8414b017fcb262e645fffc5a3129ecdf43commit-bot@chromium.org logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', 864a6ecbb8414b017fcb262e645fffc5a3129ecdf43commit-bot@chromium.org datefmt='%m/%d/%Y %H:%M:%S', 865a6ecbb8414b017fcb262e645fffc5a3129ecdf43commit-bot@chromium.org level=logging.INFO) 866f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com parser = argparse.ArgumentParser() 867f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com parser.add_argument('--actuals-dir', 868f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com help=('Directory into which we will check out the latest ' 869f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'actual GM results. If this directory does not ' 870f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'exist, it will be created. Defaults to %(default)s'), 871f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com default=DEFAULT_ACTUALS_DIR) 8720b7127635d8245de7ac704080d722d06e47621d0epoger parser.add_argument('--boto', 8730b7127635d8245de7ac704080d722d06e47621d0epoger help=('Path to .boto file giving us credentials to access ' 8740b7127635d8245de7ac704080d722d06e47621d0epoger 'Google Storage buckets. If not specified, we will ' 8750b7127635d8245de7ac704080d722d06e47621d0epoger 'only be able to access public GS buckets (and thus ' 8760b7127635d8245de7ac704080d722d06e47621d0epoger 'won\'t be able to download SKP images).'), 8770b7127635d8245de7ac704080d722d06e47621d0epoger default='') 878b144271179aaf82cb1151e9dfd8e866747402594epoger # TODO(epoger): Before https://codereview.chromium.org/310093003 , 879b144271179aaf82cb1151e9dfd8e866747402594epoger # when this tool downloaded the JSON summaries from skia-autogen, 880b144271179aaf82cb1151e9dfd8e866747402594epoger # it had an --actuals-revision the caller could specify to download 881b144271179aaf82cb1151e9dfd8e866747402594epoger # actual results as of a specific point in time. We should add similar 882b144271179aaf82cb1151e9dfd8e866747402594epoger # functionality when retrieving the summaries from Google Storage. 883defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org parser.add_argument('--builders', metavar='BUILDER_REGEX', nargs='+', 884defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org help=('Only process builders matching these regular ' 885defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org 'expressions. If unspecified, process all ' 886defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org 'builders.')) 88731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org parser.add_argument('--compare-configs', action='store_true', 88831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org help=('In addition to generating differences between ' 88931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 'expectations and actuals, also generate ' 89031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 'differences between these config pairs: ' 89131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org + str(CONFIG_PAIRS_TO_COMPARE))) 892542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com parser.add_argument('--editable', action='store_true', 8939f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger help=('Allow HTTP clients to submit new GM baselines; ' 8949f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger 'SKP baselines can be edited regardless of this ' 8959f1ae465c89bb47f4cbf972bc946f661d6d50b4eepoger 'setting.')) 896f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com parser.add_argument('--export', action='store_true', 897f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com help=('Instead of only allowing access from HTTP clients ' 898f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'on localhost, allow HTTP clients on other hosts ' 899f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'to access this server. WARNING: doing so will ' 900f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 'allow users on other hosts to modify your ' 901542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 'GM expectations, if combined with --editable.')) 902b144271179aaf82cb1151e9dfd8e866747402594epoger parser.add_argument('--gm-summaries-bucket', 903b144271179aaf82cb1151e9dfd8e866747402594epoger help=('Google Cloud Storage bucket to download ' 904b144271179aaf82cb1151e9dfd8e866747402594epoger 'JSON_FILENAME files from. ' 905b144271179aaf82cb1151e9dfd8e866747402594epoger 'Defaults to %(default)s ; if set to ' 906b144271179aaf82cb1151e9dfd8e866747402594epoger 'empty string, just compare to actual-results ' 907b144271179aaf82cb1151e9dfd8e866747402594epoger 'already found in ACTUALS_DIR.'), 908b144271179aaf82cb1151e9dfd8e866747402594epoger default=DEFAULT_GM_SUMMARIES_BUCKET) 909b144271179aaf82cb1151e9dfd8e866747402594epoger parser.add_argument('--json-filename', 910b144271179aaf82cb1151e9dfd8e866747402594epoger help=('JSON summary filename to read for each builder; ' 911b144271179aaf82cb1151e9dfd8e866747402594epoger 'defaults to %(default)s.'), 912b144271179aaf82cb1151e9dfd8e866747402594epoger default=DEFAULT_JSON_FILENAME) 913afaad3dd7002feb6e69983ddc4e5148d7803baedepoger@google.com parser.add_argument('--port', type=int, 914afaad3dd7002feb6e69983ddc4e5148d7803baedepoger@google.com help=('Which TCP port to listen on for HTTP requests; ' 915afaad3dd7002feb6e69983ddc4e5148d7803baedepoger@google.com 'defaults to %(default)s'), 916afaad3dd7002feb6e69983ddc4e5148d7803baedepoger@google.com default=DEFAULT_PORT) 917542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com parser.add_argument('--reload', type=int, 918542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com help=('How often (a period in seconds) to update the ' 919b063e13428cef96abda9a80b273c92e2ed9c8287epoger@google.com 'results. If specified, both expected and actual ' 920d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 'results will be updated by running "gclient sync" ' 921d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 'on your Skia checkout as a whole. ' 922542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 'By default, we do not reload at all, and you ' 923542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com 'must restart the server to pick up new data.'), 924542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com default=0) 9250b7127635d8245de7ac704080d722d06e47621d0epoger parser.add_argument('--threads', type=int, 9260b7127635d8245de7ac704080d722d06e47621d0epoger help=('How many parallel threads we use to download ' 9270b7127635d8245de7ac704080d722d06e47621d0epoger 'images and generate diffs; defaults to ' 9280b7127635d8245de7ac704080d722d06e47621d0epoger '%(default)s'), 9290b7127635d8245de7ac704080d722d06e47621d0epoger default=imagediffdb.DEFAULT_NUM_WORKER_THREADS) 9300b7127635d8245de7ac704080d722d06e47621d0epoger parser.add_argument('--truncate', action='store_true', 9310b7127635d8245de7ac704080d722d06e47621d0epoger help=('FOR TESTING ONLY: truncate the set of images we ' 9320b7127635d8245de7ac704080d722d06e47621d0epoger 'process, to speed up testing.')) 933f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com args = parser.parse_args() 93431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org if args.compare_configs: 93531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_pairs = CONFIG_PAIRS_TO_COMPARE 93631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org else: 93731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org config_pairs = None 93831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org 939f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com global _SERVER 940542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com _SERVER = Server(actuals_dir=args.actuals_dir, 941b144271179aaf82cb1151e9dfd8e866747402594epoger json_filename=args.json_filename, 942b144271179aaf82cb1151e9dfd8e866747402594epoger gm_summaries_bucket=args.gm_summaries_bucket, 943542b65f2347f571d1361df5a71844ebe24f1351cepoger@google.com port=args.port, export=args.export, editable=args.editable, 944defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org reload_seconds=args.reload, config_pairs=config_pairs, 9450b7127635d8245de7ac704080d722d06e47621d0epoger builder_regex_list=args.builders, boto_file_path=args.boto, 9460b7127635d8245de7ac704080d722d06e47621d0epoger imagediffdb_threads=args.threads) 9470b7127635d8245de7ac704080d722d06e47621d0epoger if args.truncate: 9480b7127635d8245de7ac704080d722d06e47621d0epoger _SERVER.truncate_results = True 949f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com _SERVER.run() 950f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com 951d6bab0238655dbab24dfe92bd0b16b464310a8c7rmistry@google.com 952f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comif __name__ == '__main__': 953f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com main() 954