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.comRepackage expected/actual GM results as needed by our HTML rebaseline viewer.
109fb6c8ac9ce7dd5d3319b4e3affd5f1e051162a2epoger@google.com"""
11f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com
12f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# System-level imports
1331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgimport fnmatch
14f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comimport os
15f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comimport re
16f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com
17b4edbffd7c09ca172f95fc30381671962e22dee0epoger# Must fix up PYTHONPATH before importing from within Skia
183b5c86c7a2db25f82a8415b6c79d1ac580fc64aestephanaimport rs_fixpypath  # pylint: disable=W0611
19b4edbffd7c09ca172f95fc30381671962e22dee0epoger
20f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.com# Imports from within Skia
21f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comimport gm_json
2231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgimport imagepairset
2316f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org
2416f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org# Keys used to link an image to a particular GM test.
2516f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org# NOTE: Keep these in sync with static/constants.js
265c4b137592a7937b4d02d72a976fa783f8d3675aepogerVALUE__HEADER__SCHEMA_VERSION = 5
2716f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__EXPECTATIONS__BUGS = gm_json.JSONKEY_EXPECTEDRESULTS_BUGS
2816f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__EXPECTATIONS__IGNOREFAILURE = gm_json.JSONKEY_EXPECTEDRESULTS_IGNOREFAILURE
2916f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__EXPECTATIONS__REVIEWED = gm_json.JSONKEY_EXPECTEDRESULTS_REVIEWED
3068a3815401f461976f76891d0477cb1440fa0abacommit-bot@chromium.orgKEY__EXTRACOLUMNS__BUILDER = 'builder'
3168a3815401f461976f76891d0477cb1440fa0abacommit-bot@chromium.orgKEY__EXTRACOLUMNS__CONFIG = 'config'
3268a3815401f461976f76891d0477cb1440fa0abacommit-bot@chromium.orgKEY__EXTRACOLUMNS__RESULT_TYPE = 'resultType'
3368a3815401f461976f76891d0477cb1440fa0abacommit-bot@chromium.orgKEY__EXTRACOLUMNS__TEST = 'test'
347498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.orgKEY__HEADER__DATAHASH = 'dataHash'
357498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.orgKEY__HEADER__IS_EDITABLE = 'isEditable'
367498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.orgKEY__HEADER__IS_EXPORTED = 'isExported'
377498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.orgKEY__HEADER__IS_STILL_LOADING = 'resultsStillLoading'
3816f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__HEADER__RESULTS_ALL = 'all'
3916f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__HEADER__RESULTS_FAILURES = 'failures'
40ea770f155e72f88e66a4d9c652552517d46a8605commit-bot@chromium.orgKEY__HEADER__SCHEMA_VERSION = 'schemaVersion'
415c4b137592a7937b4d02d72a976fa783f8d3675aepogerKEY__HEADER__SET_A_DESCRIPTIONS = 'setA'
425c4b137592a7937b4d02d72a976fa783f8d3675aepogerKEY__HEADER__SET_B_DESCRIPTIONS = 'setB'
437498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.orgKEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE = 'timeNextUpdateAvailable'
447498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.orgKEY__HEADER__TIME_UPDATED = 'timeUpdated'
457498d95bde16eeaa4b20643fb930b6a3be7face1commit-bot@chromium.orgKEY__HEADER__TYPE = 'type'
4616f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__RESULT_TYPE__FAILED = gm_json.JSONKEY_ACTUALRESULTS_FAILED
4716f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__RESULT_TYPE__FAILUREIGNORED = gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED
4816f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__RESULT_TYPE__NOCOMPARISON = gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON
4916f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.orgKEY__RESULT_TYPE__SUCCEEDED = gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED
505c4b137592a7937b4d02d72a976fa783f8d3675aepogerKEY__SET_DESCRIPTIONS__DIR = 'dir'
515c4b137592a7937b4d02d72a976fa783f8d3675aepogerKEY__SET_DESCRIPTIONS__REPO_REVISION = 'repoRevision'
525c4b137592a7937b4d02d72a976fa783f8d3675aepogerKEY__SET_DESCRIPTIONS__SECTION = 'section'
5316f418080ff6751e15e0193263149412de9c848acommit-bot@chromium.org
54f9d134da93b8c78e7127efd84c9a26f99a73527eepoger@google.comIMAGE_FILENAME_RE = re.compile(gm_json.IMAGE_FILENAME_PATTERN)
55eb832599b631a09b180ea3615347adba6bd5e363epoger@google.comIMAGE_FILENAME_FORMATTER = '%s_%s.png'  # pass in (testname, config)
5631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
57b144271179aaf82cb1151e9dfd8e866747402594epogerPARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
58defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.orgDEFAULT_ACTUALS_DIR = '.gm-actuals'
59defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.orgDEFAULT_GENERATED_IMAGES_ROOT = os.path.join(
60defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    PARENT_DIRECTORY, '.generated-images')
61defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org
62defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org# Define the default set of builders we will process expectations/actuals for.
6331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# This allows us to ignore builders for which we don't maintain expectations
6431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# (trybots, Valgrind, ASAN, TSAN), and avoid problems like
6531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# https://code.google.com/p/skia/issues/detail?id=2036 ('rebaseline_server
6631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org# produces error when trying to add baselines for ASAN/TSAN builders')
67defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.orgDEFAULT_MATCH_BUILDERS_PATTERN_LIST = ['.*']
68defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.orgDEFAULT_SKIP_BUILDERS_PATTERN_LIST = [
69defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    '.*-Trybot', '.*Valgrind.*', '.*TSAN.*', '.*ASAN.*']
7031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
7131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
7231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.orgclass BaseComparisons(object):
7331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org  """Base class for generating summary of comparisons between two image sets.
7431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org  """
7531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
765c4b137592a7937b4d02d72a976fa783f8d3675aepoger  def __init__(self):
775c4b137592a7937b4d02d72a976fa783f8d3675aepoger    """Base constructor; most subclasses will override."""
785c4b137592a7937b4d02d72a976fa783f8d3675aepoger    self._setA_descriptions = None
795c4b137592a7937b4d02d72a976fa783f8d3675aepoger    self._setB_descriptions = None
805c4b137592a7937b4d02d72a976fa783f8d3675aepoger
8131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org  def get_results_of_type(self, results_type):
8231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    """Return results of some/all tests (depending on 'results_type' parameter).
8331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
8431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    Args:
8531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      results_type: string describing which types of results to include; must
8631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org          be one of the RESULTS_* constants
8731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
8831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    Results are returned in a dictionary as output by ImagePairSet.as_dict().
8931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    """
9031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    return self._results[results_type]
9131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
9231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org  def get_packaged_results_of_type(self, results_type, reload_seconds=None,
9331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org                                   is_editable=False, is_exported=True):
9431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    """Package the results of some/all tests as a complete response_dict.
9531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
9631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    Args:
9731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      results_type: string indicating which set of results to return;
9831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org          must be one of the RESULTS_* constants
9931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      reload_seconds: if specified, note that new results may be available once
10031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org          these results are reload_seconds old
10131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      is_editable: whether clients are allowed to submit new baselines
10231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      is_exported: whether these results are being made available to other
10331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org          network hosts
10431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    """
10531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    response_dict = self._results[results_type]
10631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    time_updated = self.get_timestamp()
1075c4b137592a7937b4d02d72a976fa783f8d3675aepoger    header_dict = {
10831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        KEY__HEADER__SCHEMA_VERSION: (
10968a3815401f461976f76891d0477cb1440fa0abacommit-bot@chromium.org            VALUE__HEADER__SCHEMA_VERSION),
11031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
11131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        # Timestamps:
11231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        # 1. when this data was last updated
11331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        # 2. when the caller should check back for new data (if ever)
11431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        KEY__HEADER__TIME_UPDATED: time_updated,
11531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE: (
11631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org            (time_updated+reload_seconds) if reload_seconds else None),
11731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
11831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        # The type we passed to get_results_of_type()
11931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        KEY__HEADER__TYPE: results_type,
12031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
12131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        # Hash of dataset, which the client must return with any edits--
12231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        # this ensures that the edits were made to a particular dataset.
12331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        KEY__HEADER__DATAHASH: str(hash(repr(
12468a3815401f461976f76891d0477cb1440fa0abacommit-bot@chromium.org            response_dict[imagepairset.KEY__ROOT__IMAGEPAIRS]))),
12531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
12631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        # Whether the server will accept edits back.
12731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        KEY__HEADER__IS_EDITABLE: is_editable,
12831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
12931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        # Whether the service is accessible from other hosts.
13031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        KEY__HEADER__IS_EXPORTED: is_exported,
13131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    }
1325c4b137592a7937b4d02d72a976fa783f8d3675aepoger    if self._setA_descriptions:
1335c4b137592a7937b4d02d72a976fa783f8d3675aepoger      header_dict[KEY__HEADER__SET_A_DESCRIPTIONS] = self._setA_descriptions
1345c4b137592a7937b4d02d72a976fa783f8d3675aepoger    if self._setB_descriptions:
1355c4b137592a7937b4d02d72a976fa783f8d3675aepoger      header_dict[KEY__HEADER__SET_B_DESCRIPTIONS] = self._setB_descriptions
1365c4b137592a7937b4d02d72a976fa783f8d3675aepoger    response_dict[imagepairset.KEY__ROOT__HEADER] = header_dict
13731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    return response_dict
13831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
13931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org  def get_timestamp(self):
14031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    """Return the time at which this object was created, in seconds past epoch
14131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    (UTC).
14231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    """
14331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    return self._timestamp
14431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
145defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org  _match_builders_pattern_list = [
146defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org      re.compile(p) for p in DEFAULT_MATCH_BUILDERS_PATTERN_LIST]
147defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org  _skip_builders_pattern_list = [
148defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org      re.compile(p) for p in DEFAULT_SKIP_BUILDERS_PATTERN_LIST]
149defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org
150defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org  def set_match_builders_pattern_list(self, pattern_list):
151defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    """Override the default set of builders we should process.
152defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org
153defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    The default is DEFAULT_MATCH_BUILDERS_PATTERN_LIST .
154defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org
155defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    Note that skip_builders_pattern_list overrides this; regardless of whether a
156defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    builder is in the "match" list, if it's in the "skip" list, we will skip it.
157defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org
158defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    Args:
159defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org      pattern_list: list of regex patterns; process builders that match any
160defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org          entry within this list
161defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    """
162defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    if pattern_list == None:
163defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org      pattern_list = []
164defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    self._match_builders_pattern_list = [re.compile(p) for p in pattern_list]
165defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org
166defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org  def set_skip_builders_pattern_list(self, pattern_list):
167defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    """Override the default set of builders we should skip while processing.
168defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org
169defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    The default is DEFAULT_SKIP_BUILDERS_PATTERN_LIST .
170defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org
171defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    This overrides match_builders_pattern_list; regardless of whether a
172defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    builder is in the "match" list, if it's in the "skip" list, we will skip it.
173defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org
174defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    Args:
175defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org      pattern_list: list of regex patterns; skip builders that match any
176defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org          entry within this list
177defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    """
178defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    if pattern_list == None:
179defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org      pattern_list = []
180defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    self._skip_builders_pattern_list = [re.compile(p) for p in pattern_list]
181defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org
182defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org  def _ignore_builder(self, builder):
183defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    """Returns True if we should skip processing this builder.
18431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
18531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    Args:
18631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      builder: name of this builder, as a string
18731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
18831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    Returns:
18931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      True if we should ignore expectations and actuals for this builder.
19031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    """
191defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    for pattern in self._skip_builders_pattern_list:
19231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      if pattern.match(builder):
19331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        return True
194defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    for pattern in self._match_builders_pattern_list:
195defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org      if pattern.match(builder):
196defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org        return False
197defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org    return True
19831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
1997418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org  def _read_builder_dicts_from_root(self, root, pattern='*.json'):
20031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    """Read all JSON dictionaries within a directory tree.
20131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
2027418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    Skips any dictionaries belonging to a builder we have chosen to ignore.
2037418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org
20431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    Args:
20531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      root: path to root of directory tree
20631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      pattern: which files to read within root (fnmatch-style pattern)
20731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
20831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    Returns:
20931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      A meta-dictionary containing all the JSON dictionaries found within
2107418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org      the directory tree, keyed by builder name (the basename of the directory
2117418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org      where each JSON dictionary was found).
21231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
21331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    Raises:
21431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      IOError if root does not refer to an existing directory
21531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    """
2162c4352bb9db02db70b829f96f397faa80fade220epoger    # I considered making this call read_dicts_from_root(), but I decided
2177418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    # it was better to prune out the ignored builders within the os.walk().
21831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    if not os.path.isdir(root):
21931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      raise IOError('no directory found at path %s' % root)
22031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    meta_dict = {}
221b4edbffd7c09ca172f95fc30381671962e22dee0epoger    for dirpath, _, filenames in os.walk(root):
22231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      for matching_filename in fnmatch.filter(filenames, pattern):
22331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        builder = os.path.basename(dirpath)
224defe6fdbc8edb2df0887c007450a8d8cc446f420commit-bot@chromium.org        if self._ignore_builder(builder):
22531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org          continue
2267418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org        full_path = os.path.join(dirpath, matching_filename)
2277418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org        meta_dict[builder] = gm_json.LoadFromFile(full_path)
2287418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    return meta_dict
2297418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org
2302c4352bb9db02db70b829f96f397faa80fade220epoger  @staticmethod
2312c4352bb9db02db70b829f96f397faa80fade220epoger  def read_dicts_from_root(root, pattern='*.json'):
2327418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    """Read all JSON dictionaries within a directory tree.
2337418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org
2342c4352bb9db02db70b829f96f397faa80fade220epoger    TODO(stephana): Factor this out into a utility module, as a standalone
2352c4352bb9db02db70b829f96f397faa80fade220epoger    function (not part of a class).
2362c4352bb9db02db70b829f96f397faa80fade220epoger
2377418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    Args:
2387418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org      root: path to root of directory tree
2397418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org      pattern: which files to read within root (fnmatch-style pattern)
2407418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org
2417418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    Returns:
2427418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org      A meta-dictionary containing all the JSON dictionaries found within
2437418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org      the directory tree, keyed by the pathname (relative to root) of each JSON
2447418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org      dictionary.
2457418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org
2467418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    Raises:
2477418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org      IOError if root does not refer to an existing directory
2487418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    """
2497418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    if not os.path.isdir(root):
2507418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org      raise IOError('no directory found at path %s' % root)
2517418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    meta_dict = {}
252b4edbffd7c09ca172f95fc30381671962e22dee0epoger    for abs_dirpath, _, filenames in os.walk(root):
2537418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org      rel_dirpath = os.path.relpath(abs_dirpath, root)
2547418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org      for matching_filename in fnmatch.filter(filenames, pattern):
2557418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org        abs_path = os.path.join(abs_dirpath, matching_filename)
2567418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org        rel_path = os.path.join(rel_dirpath, matching_filename)
2577418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org        meta_dict[rel_path] = gm_json.LoadFromFile(abs_path)
25831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    return meta_dict
25931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
26031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org  @staticmethod
2614cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org  def _read_noncomment_lines(path):
2624cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org    """Return a list of all noncomment lines within a file.
2634cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org
2644cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org    (A "noncomment" line is one that does not start with a '#'.)
2654cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org
2664cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org    Args:
2674cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org      path: path to file
2684cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org    """
2694cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org    lines = []
2704cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org    with open(path, 'r') as fh:
2714cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org      for line in fh:
2724cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org        if not line.startswith('#'):
2734cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org          lines.append(line.strip())
2744cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org    return lines
2754cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org
2764cef1be82193a9d527f7b1f873197c443d0dde2ecommit-bot@chromium.org  @staticmethod
27731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org  def _create_relative_url(hashtype_and_digest, test_name):
27831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    """Returns the URL for this image, relative to GM_ACTUALS_ROOT_HTTP_URL.
27931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
28031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    If we don't have a record of this image, returns None.
28131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org
28231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    Args:
28331d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      hashtype_and_digest: (hash_type, hash_digest) tuple, or None if we
28431d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org          don't have a record of this image
28531d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      test_name: string; name of the GM test that created this image
28631d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    """
28731d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    if not hashtype_and_digest:
28831d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org      return None
28931d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org    return gm_json.CreateGmRelativeUrl(
29031d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        test_name=test_name,
29131d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        hash_type=hashtype_and_digest[0],
29231d0b3d806a1aa86b7edaa442b3821f5d548e184commit-bot@chromium.org        hash_digest=hashtype_and_digest[1])
2933eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org
2943eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org  @staticmethod
2953eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org  def combine_subdicts(input_dict):
2963eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org    """ Flatten out a dictionary structure by one level.
2973eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org
2983eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org    Input:
2993eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org      {
3007418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org        KEY_A1 : {
3017418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org          KEY_B1 : VALUE_B1,
3023eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org        },
3037418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org        KEY_A2 : {
3047418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org          KEY_B2 : VALUE_B2,
3053eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org        }
3063eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org      }
3073eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org
3083eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org    Output:
3093eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org      {
3107418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org        KEY_B1 : VALUE_B1,
3117418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org        KEY_B2 : VALUE_B2,
3123eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org      }
3133eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org
3143eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org    If this would result in any repeated keys, it will raise an Exception.
3153eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org    """
3163eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org    output_dict = {}
317b4edbffd7c09ca172f95fc30381671962e22dee0epoger    for subdict in input_dict.values():
3183eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org      for subdict_key, subdict_value in subdict.iteritems():
3193eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org        if subdict_key in output_dict:
3203eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org          raise Exception('duplicate key %s in combine_subdicts' % subdict_key)
3213eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org        output_dict[subdict_key] = subdict_value
3223eb77e4d5a381fa55197f6bd03c599e709146069commit-bot@chromium.org    return output_dict
3237418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org
3247418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org  @staticmethod
3250b7127635d8245de7ac704080d722d06e47621d0epoger  def get_default(input_dict, default_value, *keys):
3260b7127635d8245de7ac704080d722d06e47621d0epoger    """Returns input_dict[key1][key2][...], or default_value.
3270b7127635d8245de7ac704080d722d06e47621d0epoger
3280b7127635d8245de7ac704080d722d06e47621d0epoger    If input_dict is None, or any key is missing along the way, this returns
3290b7127635d8245de7ac704080d722d06e47621d0epoger    default_value.
3300b7127635d8245de7ac704080d722d06e47621d0epoger
3310b7127635d8245de7ac704080d722d06e47621d0epoger    Args:
3320b7127635d8245de7ac704080d722d06e47621d0epoger      input_dict: dictionary to look within
3330b7127635d8245de7ac704080d722d06e47621d0epoger      key: key indicating which value to return from input_dict
3340b7127635d8245de7ac704080d722d06e47621d0epoger      default_value: value to return if input_dict is None or any key cannot
3350b7127635d8245de7ac704080d722d06e47621d0epoger          be found along the way
3367418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    """
3370b7127635d8245de7ac704080d722d06e47621d0epoger    if input_dict == None:
3380b7127635d8245de7ac704080d722d06e47621d0epoger      return default_value
3397418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    for key in keys:
3407418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org      input_dict = input_dict.get(key, None)
3410b7127635d8245de7ac704080d722d06e47621d0epoger      if input_dict == None:
3420b7127635d8245de7ac704080d722d06e47621d0epoger        return default_value
3437418bd8cad3576294b48dd8e5015301e184c6af1commit-bot@chromium.org    return input_dict
344