1363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#!/usr/bin/python
2363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
3363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger'''
4363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek SollenbergerCopyright 2012 Google Inc.
5363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
6363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek SollenbergerUse of this source code is governed by a BSD-style license that can be
7363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerfound in the LICENSE file.
8363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger'''
9363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
10363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger'''
11363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek SollenbergerRebaselines the given GM tests, on all bots and all configurations.
12363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger'''
13363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
1458190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# System-level imports
1558190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerimport argparse
160a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerimport json
1758190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerimport os
1858190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerimport re
1958190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerimport subprocess
2058190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerimport sys
2158190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerimport urllib2
2258190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
2358190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# Imports from within Skia
2458190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger#
2558190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# We need to add the 'gm' directory, so that we can import gm_json.py within
2658190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# that directory.  That script allows us to parse the actual-results.json file
2758190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# written out by the GM tool.
2858190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end*
2958190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# so any dirs that are already in the PYTHONPATH will be preferred.
3058190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger#
3158190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# This assumes that the 'gm' directory has been checked out as a sibling of
3258190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# the 'tools' directory containing this script, which will be the case if
3358190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# 'trunk' was checked out as a single unit.
3458190644c30e1c4aa8e527f3503c58f841e0fcf3Derek SollenbergerGM_DIRECTORY = os.path.realpath(
3558190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger    os.path.join(os.path.dirname(os.path.dirname(__file__)), 'gm'))
3658190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerif GM_DIRECTORY not in sys.path:
37e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  sys.path.append(GM_DIRECTORY)
3858190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerimport gm_json
3958190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
400a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger# TODO(epoger): In the long run, we want to build this list automatically,
410a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger# but for now we hard-code it until we can properly address
420a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger# https://code.google.com/p/skia/issues/detail?id=1544
430a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger# ('live query of builder list makes rebaseline.py slow to start up')
440a657bbc2c6fc9daf699942e023050536d5ec95fDerek SollenbergerTEST_BUILDERS = [
4558190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger    'Test-Android-GalaxyNexus-SGX540-Arm7-Debug',
460a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Android-GalaxyNexus-SGX540-Arm7-Release',
470a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Android-IntelRhb-SGX544-x86-Debug',
480a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Android-IntelRhb-SGX544-x86-Release',
490a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Android-Nexus10-MaliT604-Arm7-Debug',
500a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Android-Nexus10-MaliT604-Arm7-Release',
510a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Android-Nexus4-Adreno320-Arm7-Debug',
520a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Android-Nexus4-Adreno320-Arm7-Release',
530a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Android-Nexus7-Tegra3-Arm7-Debug',
5458190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger    'Test-Android-Nexus7-Tegra3-Arm7-Release',
550a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Android-NexusS-SGX540-Arm7-Debug',
5658190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger    'Test-Android-NexusS-SGX540-Arm7-Release',
570a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Android-Xoom-Tegra2-Arm7-Debug',
5858190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger    'Test-Android-Xoom-Tegra2-Arm7-Release',
590a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-ChromeOS-Alex-GMA3150-x86-Debug',
600a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-ChromeOS-Alex-GMA3150-x86-Release',
610a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-ChromeOS-Daisy-MaliT604-Arm7-Debug',
620a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-ChromeOS-Daisy-MaliT604-Arm7-Release',
630a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-ChromeOS-Link-HD4000-x86_64-Debug',
640a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-ChromeOS-Link-HD4000-x86_64-Release',
650a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug',
660a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Mac10.6-MacMini4.1-GeForce320M-x86-Release',
670a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Mac10.6-MacMini4.1-GeForce320M-x86_64-Debug',
680a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Mac10.6-MacMini4.1-GeForce320M-x86_64-Release',
690a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Mac10.7-MacMini4.1-GeForce320M-x86-Debug',
700a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Mac10.7-MacMini4.1-GeForce320M-x86-Release',
710a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug',
720a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Release',
730a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Mac10.8-MacMini4.1-GeForce320M-x86-Debug',
740a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Mac10.8-MacMini4.1-GeForce320M-x86-Release',
750a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Mac10.8-MacMini4.1-GeForce320M-x86_64-Debug',
760a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Mac10.8-MacMini4.1-GeForce320M-x86_64-Release',
770a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Ubuntu12-ShuttleA-ATI5770-x86-Debug',
780a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Ubuntu12-ShuttleA-ATI5770-x86-Release',
790a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Ubuntu12-ShuttleA-ATI5770-x86_64-Debug',
800a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Ubuntu12-ShuttleA-ATI5770-x86_64-Release',
810a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Ubuntu12-ShuttleA-HD2000-x86_64-Release-Valgrind',
820a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Ubuntu12-ShuttleA-NoGPU-x86_64-Debug',
830a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Ubuntu13-ShuttleA-HD2000-x86_64-Debug-ASAN',
840a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Win7-ShuttleA-HD2000-x86-Debug',
850a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Win7-ShuttleA-HD2000-x86-Debug-ANGLE',
860a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Win7-ShuttleA-HD2000-x86-Debug-DirectWrite',
870a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Win7-ShuttleA-HD2000-x86-Release',
880a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Win7-ShuttleA-HD2000-x86-Release-ANGLE',
890a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Win7-ShuttleA-HD2000-x86-Release-DirectWrite',
900a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Win7-ShuttleA-HD2000-x86_64-Debug',
910a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    'Test-Win7-ShuttleA-HD2000-x86_64-Release',
920a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger]
930a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger
940a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger# TODO: Get this from builder_name_schema in buildbot.
950a657bbc2c6fc9daf699942e023050536d5ec95fDerek SollenbergerTRYBOT_SUFFIX = '-Trybot'
9658190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
9758190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
9858190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerclass _InternalException(Exception):
99e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  pass
10058190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
10158190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerclass ExceptionHandler(object):
1020a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  """ Object that handles exceptions, either raising them immediately or
1030a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  collecting them to display later on."""
10458190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
105e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # params:
106e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  def __init__(self, keep_going_on_failure=False):
1070a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    """
1080a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    params:
1090a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      keep_going_on_failure: if False, report failures and quit right away;
1100a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                             if True, collect failures until
1110a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                             ReportAllFailures() is called
1120a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    """
113e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    self._keep_going_on_failure = keep_going_on_failure
114e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    self._failures_encountered = []
115e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
1160a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  def RaiseExceptionOrContinue(self):
1170a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    """ We have encountered an exception; either collect the info and keep
1180a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    going, or exit the program right away."""
1190a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    # Get traceback information about the most recently raised exception.
1200a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    exc_info = sys.exc_info()
121e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
122e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    if self._keep_going_on_failure:
1230a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      print >> sys.stderr, ('WARNING: swallowing exception %s' %
1240a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                            repr(exc_info[1]))
1250a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      self._failures_encountered.append(exc_info)
126e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    else:
127e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      print >> sys.stderr, (
1280a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          '\nHalting at first exception.\n' +
1290a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          'Please file a bug to epoger@google.com at ' +
1300a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          'https://code.google.com/p/skia/issues/entry, containing the ' +
1310a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          'command you ran and the following stack trace.\n\n' +
1320a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          'Afterwards, you can re-run with the --keep-going-on-failure ' +
1330a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          'option set.\n')
1340a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      raise exc_info[1], None, exc_info[2]
13558190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
136e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  def ReportAllFailures(self):
137e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    if self._failures_encountered:
138e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      print >> sys.stderr, ('Encountered %d failures (see above).' %
139e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger                            len(self._failures_encountered))
1400a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      sys.exit(1)
14158190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
14258190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
14358190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# Object that rebaselines a JSON expectations file (not individual image files).
14458190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerclass JsonRebaseliner(object):
14558190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
146e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # params:
147e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  expectations_root: root directory of all expectations JSON files
148e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  expectations_input_filename: filename (under expectations_root) of JSON
149e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #                               expectations file to read; typically
150e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #                               "expected-results.json"
151e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  expectations_output_filename: filename (under expectations_root) to
152e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #                                which updated expectations should be
153e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #                                written; typically the same as
154e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #                                expectations_input_filename, to overwrite
155e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #                                the old content
156e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  actuals_base_url: base URL from which to read actual-result JSON files
157e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  actuals_filename: filename (under actuals_base_url) from which to read a
158e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #                    summary of results; typically "actual-results.json"
159e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  exception_handler: reference to rebaseline.ExceptionHandler object
160e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  tests: list of tests to rebaseline, or None if we should rebaseline
161e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #         whatever files the JSON results summary file tells us to
162e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  configs: which configs to run for each test, or None if we should
163e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #           rebaseline whatever configs the JSON results summary file tells
164e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #           us to
165e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  add_new: if True, add expectations for tests which don't have any yet
1660a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #  add_ignored: if True, add expectations for tests for which failures are
1670a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #               currently ignored
1680a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #  bugs: optional list of bug numbers which pertain to these expectations
1690a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #  notes: free-form text notes to add to all updated expectations
1700a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #  mark_unreviewed: if True, mark these expectations as NOT having been
1710a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #                   reviewed by a human; otherwise, leave that field blank.
1720a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #                   Currently, there is no way to make this script mark
1730a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #                   expectations as reviewed-by-human=True.
1740a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #                   TODO(epoger): Add that capability to a review tool.
1750a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #  mark_ignore_failure: if True, mark failures of a given test as being
1760a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #                       ignored.
1770a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #  from_trybot: if True, read actual-result JSON files generated from a
1780a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  #               trybot run rather than a waterfall run.
179e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  def __init__(self, expectations_root, expectations_input_filename,
180e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger               expectations_output_filename, actuals_base_url,
181e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger               actuals_filename, exception_handler,
1820a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger               tests=None, configs=None, add_new=False, add_ignored=False,
1830a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger               bugs=None, notes=None, mark_unreviewed=None,
1840a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger               mark_ignore_failure=False, from_trybot=False):
185e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    self._expectations_root = expectations_root
186e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    self._expectations_input_filename = expectations_input_filename
187e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    self._expectations_output_filename = expectations_output_filename
188e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    self._tests = tests
189e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    self._configs = configs
190e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    self._actuals_base_url = actuals_base_url
191e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    self._actuals_filename = actuals_filename
192e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    self._exception_handler = exception_handler
193e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    self._add_new = add_new
1940a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    self._add_ignored = add_ignored
1950a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    self._bugs = bugs
1960a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    self._notes = notes
1970a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    self._mark_unreviewed = mark_unreviewed
1980a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    self._mark_ignore_failure = mark_ignore_failure;
1990a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    if self._tests or self._configs:
2000a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      self._image_filename_re = re.compile(gm_json.IMAGE_FILENAME_PATTERN)
2010a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    else:
2020a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      self._image_filename_re = None
203e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    self._using_svn = os.path.isdir(os.path.join(expectations_root, '.svn'))
2040a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    self._from_trybot = from_trybot
205e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
206e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # Executes subprocess.call(cmd).
207e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # Raises an Exception if the command fails.
208e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  def _Call(self, cmd):
209e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    if subprocess.call(cmd) != 0:
210e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      raise _InternalException('error running command: ' + ' '.join(cmd))
211e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
212e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # Returns the full contents of filepath, as a single string.
213e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # If filepath looks like a URL, try to read it that way instead of as
214e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # a path on local storage.
215e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #
216e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # Raises _InternalException if there is a problem.
217e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  def _GetFileContents(self, filepath):
218e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    if filepath.startswith('http:') or filepath.startswith('https:'):
219e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      try:
220e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        return urllib2.urlopen(filepath).read()
221e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      except urllib2.HTTPError as e:
222e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        raise _InternalException('unable to read URL %s: %s' % (
223e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger            filepath, e))
224e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    else:
225e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      return open(filepath, 'r').read()
226e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
227e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # Returns a dictionary of actual results from actual-results.json file.
228e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #
229e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # The dictionary returned has this format:
230e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # {
231e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  u'imageblur_565.png': [u'bitmap-64bitMD5', 3359963596899141322],
232e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  u'imageblur_8888.png': [u'bitmap-64bitMD5', 4217923806027861152],
233e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  u'shadertext3_8888.png': [u'bitmap-64bitMD5', 3713708307125704716]
234e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # }
235e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #
236e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # If the JSON actual result summary file cannot be loaded, logs a warning
237e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # message and returns None.
238e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # If the JSON actual result summary file can be loaded, but we have
239e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # trouble parsing it, raises an Exception.
240e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #
241e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # params:
242e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  json_url: URL pointing to a JSON actual result summary file
243e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  sections: a list of section names to include in the results, e.g.
244e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #            [gm_json.JSONKEY_ACTUALRESULTS_FAILED,
245e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #             gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON] ;
246e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #            if None, then include ALL sections.
247e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  def _GetActualResults(self, json_url, sections=None):
248e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    try:
249e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      json_contents = self._GetFileContents(json_url)
250e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    except _InternalException:
251e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      print >> sys.stderr, (
252e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger          'could not read json_url %s ; skipping this platform.' %
253e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger          json_url)
254e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      return None
255e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    json_dict = gm_json.LoadFromString(json_contents)
256e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    results_to_return = {}
257e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    actual_results = json_dict[gm_json.JSONKEY_ACTUALRESULTS]
258e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    if not sections:
259e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      sections = actual_results.keys()
260e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    for section in sections:
261e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      section_results = actual_results[section]
262e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      if section_results:
263e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        results_to_return.update(section_results)
264e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    return results_to_return
265e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
266e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # Rebaseline all tests/types we specified in the constructor,
2670a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  # within this builder's subdirectory in expectations/gm .
268e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #
269e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # params:
270e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #  builder : e.g. 'Test-Win7-ShuttleA-HD2000-x86-Release'
2710a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  def RebaselineSubdir(self, builder):
272e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # Read in the actual result summary, and extract all the tests whose
273e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # results we need to update.
2740a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    results_builder = str(builder)
2750a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    if self._from_trybot:
2760a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      results_builder = results_builder + TRYBOT_SUFFIX
2770a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    actuals_url = '/'.join([self._actuals_base_url, results_builder,
278e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger                            self._actuals_filename])
2790a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    # Only update results for tests that are currently failing.
2800a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    # We don't want to rewrite results for tests that are already succeeding,
2810a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    # because we don't want to add annotation fields (such as
2820a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    # JSONKEY_EXPECTEDRESULTS_BUGS) except for tests whose expectations we
2830a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    # are actually modifying.
2840a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    sections = [gm_json.JSONKEY_ACTUALRESULTS_FAILED]
285e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    if self._add_new:
286e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      sections.append(gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON)
2870a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    if self._add_ignored:
2880a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      sections.append(gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED)
289e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    results_to_update = self._GetActualResults(json_url=actuals_url,
290e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger                                               sections=sections)
291e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
292e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # Read in current expectations.
293e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    expectations_input_filepath = os.path.join(
2940a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        self._expectations_root, builder, self._expectations_input_filename)
295e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    expectations_dict = gm_json.LoadFromFile(expectations_input_filepath)
2960a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    expected_results = expectations_dict.get(gm_json.JSONKEY_EXPECTEDRESULTS)
2970a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    if not expected_results:
2980a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      expected_results = {}
2990a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      expectations_dict[gm_json.JSONKEY_EXPECTEDRESULTS] = expected_results
300e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
301e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # Update the expectations in memory, skipping any tests/configs that
302e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # the caller asked to exclude.
303e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    skipped_images = []
304e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    if results_to_update:
305e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      for (image_name, image_results) in results_to_update.iteritems():
3060a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        if self._image_filename_re:
3070a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          (test, config) = self._image_filename_re.match(image_name).groups()
3080a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          if self._tests:
3090a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger            if test not in self._tests:
3100a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger              skipped_images.append(image_name)
3110a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger              continue
3120a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          if self._configs:
3130a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger            if config not in self._configs:
3140a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger              skipped_images.append(image_name)
3150a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger              continue
316e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        if not expected_results.get(image_name):
317e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger          expected_results[image_name] = {}
3180a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        expected_results[image_name]\
3190a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                        [gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS]\
3200a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                        = [image_results]
3210a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        if self._mark_unreviewed:
3220a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          expected_results[image_name]\
3230a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          [gm_json.JSONKEY_EXPECTEDRESULTS_REVIEWED]\
3240a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          = False
3250a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        if self._mark_ignore_failure:
3260a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          expected_results[image_name]\
3270a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          [gm_json.JSONKEY_EXPECTEDRESULTS_IGNOREFAILURE]\
3280a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          = True
3290a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        if self._bugs:
3300a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          expected_results[image_name]\
3310a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          [gm_json.JSONKEY_EXPECTEDRESULTS_BUGS]\
3320a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          = self._bugs
3330a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        if self._notes:
3340a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger          expected_results[image_name]\
3350a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          [gm_json.JSONKEY_EXPECTEDRESULTS_NOTES]\
3360a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          = self._notes
33758190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
338e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # Write out updated expectations.
339e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    expectations_output_filepath = os.path.join(
3400a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        self._expectations_root, builder, self._expectations_output_filename)
341e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    gm_json.WriteToFile(expectations_dict, expectations_output_filepath)
34258190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
343e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # Mark the JSON file as plaintext, so text-style diffs can be applied.
344e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # Fixes https://code.google.com/p/skia/issues/detail?id=1442
345e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    if self._using_svn:
346e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      self._Call(['svn', 'propset', '--quiet', 'svn:mime-type',
347e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger                  'text/x-json', expectations_output_filepath])
34858190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
34958190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# main...
35058190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
3510a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerparser = argparse.ArgumentParser(
3520a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    formatter_class=argparse.RawDescriptionHelpFormatter,
3530a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    epilog='Here is the full set of builders we know about:' +
3540a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger           '\n    '.join([''] + sorted(TEST_BUILDERS)))
35558190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerparser.add_argument('--actuals-base-url',
3560a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('base URL from which to read files containing JSON '
3570a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'summaries of actual GM results; defaults to '
3580a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          '%(default)s. To get a specific revision (useful for '
3590a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'trybots) replace "svn" with "svn-history/r123". '
3600a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'If SKIMAGE is True, defaults to ' +
3610a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          gm_json.SKIMAGE_ACTUALS_BASE_URL),
36258190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger                    default='http://skia-autogen.googlecode.com/svn/gm-actual')
36358190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerparser.add_argument('--actuals-filename',
3640a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('filename (within builder-specific subdirectories '
3650a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'of ACTUALS_BASE_URL) to read a summary of results '
3660a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'from; defaults to %(default)s'),
36758190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger                    default='actual-results.json')
36858190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerparser.add_argument('--add-new', action='store_true',
3690a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('in addition to the standard behavior of '
3700a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'updating expectations for failing tests, add '
3710a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'expectations for tests which don\'t have '
3720a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'expectations yet.'))
3730a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerparser.add_argument('--add-ignored', action='store_true',
3740a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('in addition to the standard behavior of '
3750a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'updating expectations for failing tests, add '
3760a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'expectations for tests for which failures are '
3770a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'currently ignored.'))
3780a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerparser.add_argument('--bugs', metavar='BUG', type=int, nargs='+',
3790a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('Skia bug numbers (under '
3800a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'https://code.google.com/p/skia/issues/list ) which '
3810a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'pertain to this set of rebaselines.'))
3820a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerparser.add_argument('--builders', metavar='BUILDER', nargs='+',
3830a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('which platforms to rebaseline; '
3840a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'if unspecified, rebaseline all known platforms '
3850a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          '(see below for a list)'))
38658190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# TODO(epoger): Add test that exercises --configs argument.
38758190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerparser.add_argument('--configs', metavar='CONFIG', nargs='+',
3880a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('which configurations to rebaseline, e.g. '
3890a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          '"--configs 565 8888", as a filter over the full set '
3900a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'of results in ACTUALS_FILENAME; if unspecified, '
3910a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'rebaseline *all* configs that are available.'))
39258190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerparser.add_argument('--expectations-filename',
3930a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('filename (under EXPECTATIONS_ROOT) to read '
3940a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'current expectations from, and to write new '
3950a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'expectations into (unless a separate '
3960a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'EXPECTATIONS_FILENAME_OUTPUT has been specified); '
3970a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'defaults to %(default)s'),
39858190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger                    default='expected-results.json')
39958190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerparser.add_argument('--expectations-filename-output',
4000a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('filename (under EXPECTATIONS_ROOT) to write '
4010a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'updated expectations into; by default, overwrites '
4020a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'the input file (EXPECTATIONS_FILENAME)'),
40358190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger                    default='')
40458190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerparser.add_argument('--expectations-root',
4050a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('root of expectations directory to update-- should '
4060a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'contain one or more builder subdirectories. '
4070a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'Defaults to %(default)s. If SKIMAGE is set, '
4080a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          ' defaults to ' + gm_json.SKIMAGE_EXPECTATIONS_ROOT),
40958190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger                    default=os.path.join('expectations', 'gm'))
41058190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerparser.add_argument('--keep-going-on-failure', action='store_true',
4110a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('instead of halting at the first error encountered, '
4120a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'keep going and rebaseline as many tests as '
4130a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'possible, and then report the full set of errors '
4140a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'at the end'))
4150a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerparser.add_argument('--notes',
4160a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('free-form text notes to add to all updated '
4170a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'expectations'))
41858190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger# TODO(epoger): Add test that exercises --tests argument.
41958190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerparser.add_argument('--tests', metavar='TEST', nargs='+',
4200a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('which tests to rebaseline, e.g. '
4210a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          '"--tests aaclip bigmatrix", as a filter over the '
4220a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'full set of results in ACTUALS_FILENAME; if '
4230a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'unspecified, rebaseline *all* tests that are '
4240a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'available.'))
4250a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerparser.add_argument('--unreviewed', action='store_true',
4260a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('mark all expectations modified by this run as '
4270a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          '"%s": False' %
4280a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          gm_json.JSONKEY_EXPECTEDRESULTS_REVIEWED))
4290a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerparser.add_argument('--ignore-failure', action='store_true',
4300a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('mark all expectations modified by this run as '
4310a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          '"%s": True' %
4320a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED))
4330a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerparser.add_argument('--from-trybot', action='store_true',
4340a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('pull the actual-results.json file from the '
4350a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'corresponding trybot, rather than the main builder'))
4360a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerparser.add_argument('--skimage', action='store_true',
4370a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                    help=('Rebaseline skimage results instead of gm. Defaults '
4380a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'to False. If True, TESTS and CONFIGS are ignored, '
4390a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'and ACTUALS_BASE_URL and EXPECTATIONS_ROOT are set '
4400a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                          'to alternate defaults, specific to skimage.'))
44158190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerargs = parser.parse_args()
44258190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerexception_handler = ExceptionHandler(
44358190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger    keep_going_on_failure=args.keep_going_on_failure)
4440a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerif args.builders:
4450a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  builders = args.builders
446e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  missing_json_is_fatal = True
44758190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerelse:
4480a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  builders = sorted(TEST_BUILDERS)
449e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  missing_json_is_fatal = False
4500a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerif args.skimage:
4510a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  # Use a different default if --skimage is specified.
4520a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  if args.actuals_base_url == parser.get_default('actuals_base_url'):
4530a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    args.actuals_base_url = gm_json.SKIMAGE_ACTUALS_BASE_URL
4540a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  if args.expectations_root == parser.get_default('expectations_root'):
4550a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    args.expectations_root = gm_json.SKIMAGE_EXPECTATIONS_ROOT
4560a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenbergerfor builder in builders:
4570a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  if not builder in TEST_BUILDERS:
4580a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    raise Exception(('unrecognized builder "%s"; ' +
459e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger                     'should be one of %s') % (
4600a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                         builder, TEST_BUILDERS))
461e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
4620a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger  expectations_json_file = os.path.join(args.expectations_root, builder,
463e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger                                        args.expectations_filename)
464e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  if os.path.isfile(expectations_json_file):
465e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    rebaseliner = JsonRebaseliner(
466e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        expectations_root=args.expectations_root,
467e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        expectations_input_filename=args.expectations_filename,
468e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        expectations_output_filename=(args.expectations_filename_output or
469e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger                                      args.expectations_filename),
470e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        tests=args.tests, configs=args.configs,
471e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        actuals_base_url=args.actuals_base_url,
472e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        actuals_filename=args.actuals_filename,
473e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        exception_handler=exception_handler,
4740a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        add_new=args.add_new, add_ignored=args.add_ignored,
4750a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        bugs=args.bugs, notes=args.notes,
4760a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        mark_unreviewed=args.unreviewed,
4770a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        mark_ignore_failure=args.ignore_failure,
4780a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger        from_trybot=args.from_trybot)
47958190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger    try:
4800a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      rebaseliner.RebaselineSubdir(builder=builder)
4810a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    except:
4820a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      exception_handler.RaiseExceptionOrContinue()
483e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  else:
4840a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    try:
4850a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      raise _InternalException('expectations_json_file %s not found' %
4860a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger                               expectations_json_file)
4870a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger    except:
4880a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger      exception_handler.RaiseExceptionOrContinue()
48958190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenberger
49058190644c30e1c4aa8e527f3503c58f841e0fcf3Derek Sollenbergerexception_handler.ReportAllFailures()
491