rebaseline.py: split image-based rebaselining, which will go away soon, into its own script
(part of step 3 in https://goto.google.com/ChecksumTransitionDetail )

also adds new --expectations-root option

R=scroggo@google.com, senorblanco@chromium.org

Review URL: https://codereview.chromium.org/17379004

git-svn-id: http://skia.googlecode.com/svn/trunk@9689 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/tools/rebaseline.py b/tools/rebaseline.py
index e270e90..cf72602 100755
--- a/tools/rebaseline.py
+++ b/tools/rebaseline.py
@@ -21,6 +21,9 @@
 import sys
 import urllib2
 
+# Imports from local directory
+import rebaseline_imagefiles
+
 # Imports from within Skia
 #
 # We need to add the 'gm' directory, so that we can import gm_json.py within
@@ -38,6 +41,7 @@
     sys.path.append(GM_DIRECTORY)
 import gm_json
 
+JSON_EXPECTATIONS_FILENAME='expected-results.json'
 
 # Mapping of gm-expectations subdir (under
 # https://skia.googlecode.com/svn/gm-expected/ )
@@ -71,14 +75,19 @@
 class CommandFailedException(Exception):
     pass
 
-class Rebaseliner(object):
+# Object that rebaselines a JSON expectations file (not individual image files).
+#
+# TODO(epoger): Most of this is just the code from the old ImageRebaseliner...
+# some of it will need to be updated in order to properly rebaseline JSON files.
+# There is a lot of code duplicated between here and ImageRebaseliner, but
+# that's fine because we will delete ImageRebaseliner soon.
+class JsonRebaseliner(object):
 
     # params:
+    #  expectations_root: root directory of all expectations
     #  json_base_url: base URL from which to read json_filename
     #  json_filename: filename (under json_base_url) from which to read a
     #                 summary of results; typically "actual-results.json"
-    #  subdirs: which platform subdirectories to rebaseline; if not specified,
-    #           rebaseline all platform subdirectories
     #  tests: list of tests to rebaseline, or None if we should rebaseline
     #         whatever files the JSON results summary file tells us to
     #  configs: which configs to run for each test; this should only be
@@ -88,24 +97,23 @@
     #           files to checkout, display a list of operations that
     #           we would normally perform
     #  add_new: if True, add expectations for tests which don't have any yet
-    def __init__(self, json_base_url, json_filename,
-                 subdirs=None, tests=None, configs=None, dry_run=False,
-                 add_new=False):
+    #  missing_json_is_fatal: whether to halt execution if we cannot read a
+    #                         JSON actual result summary file
+    def __init__(self, expectations_root, json_base_url, json_filename,
+                 tests=None, configs=None, dry_run=False,
+                 add_new=False, missing_json_is_fatal=False):
+        raise ValueError('JsonRebaseliner not yet implemented') # TODO(epoger)
         if configs and not tests:
             raise ValueError('configs should only be specified if tests ' +
                              'were specified also')
+        self._expectations_root = expectations_root
         self._tests = tests
         self._configs = configs
-        if not subdirs:
-            self._subdirs = sorted(SUBDIR_MAPPING.keys())
-            self._missing_json_is_fatal = False
-        else:
-            self._subdirs = subdirs
-            self._missing_json_is_fatal = True
         self._json_base_url = json_base_url
         self._json_filename = json_filename
         self._dry_run = dry_run
         self._add_new = add_new
+        self._missing_json_is_fatal = missing_json_is_fatal
         self._googlestorage_gm_actuals_root = (
             'http://chromium-skia-gm.commondatastorage.googleapis.com/gm')
         self._testname_pattern = re.compile('(\S+)_(\S+).png')
@@ -339,34 +347,33 @@
                                     outfilename=outfilename,
                                     all_results=all_results)
 
-    # Rebaseline all platforms/tests/types we specified in the constructor.
-    def RebaselineAll(self):
-        for subdir in self._subdirs:
-            if not subdir in SUBDIR_MAPPING.keys():
-                raise Exception(('unrecognized platform subdir "%s"; ' +
-                                 'should be one of %s') % (
-                                     subdir, SUBDIR_MAPPING.keys()))
-            builder_name = SUBDIR_MAPPING[subdir]
-            json_url = '/'.join([self._json_base_url,
-                                 subdir, builder_name, subdir,
-                                 self._json_filename])
-            all_results = self._GetActualResults(json_url=json_url)
+    # Rebaseline all tests/types we specified in the constructor,
+    # within this gm-expectations subdir.
+    #
+    # params:
+    #  subdir : e.g. 'base-shuttle-win7-intel-float'
+    #  builder : e.g. 'Test-Win7-ShuttleA-HD2000-x86-Release'
+    def RebaselineSubdir(self, subdir, builder):
+        json_url = '/'.join([self._json_base_url,
+                             subdir, builder, subdir,
+                             self._json_filename])
+        all_results = self._GetActualResults(json_url=json_url)
 
-            if self._tests:
-                for test in self._tests:
-                    self._RebaselineOneTest(expectations_subdir=subdir,
-                                            builder_name=builder_name,
-                                            test=test, all_results=all_results)
-            else:  # get the raw list of files that need rebaselining from JSON
-                filenames = self._GetFilesToRebaseline(json_url=json_url,
-                                                       add_new=self._add_new)
-                for filename in filenames:
-                    outfilename = os.path.join(subdir, filename);
-                    self._RebaselineOneFile(expectations_subdir=subdir,
-                                            builder_name=builder_name,
-                                            infilename=filename,
-                                            outfilename=outfilename,
-                                            all_results=all_results)
+        if self._tests:
+            for test in self._tests:
+                self._RebaselineOneTest(expectations_subdir=subdir,
+                                        builder_name=builder,
+                                        test=test, all_results=all_results)
+        else:  # get the raw list of files that need rebaselining from JSON
+            filenames = self._GetFilesToRebaseline(json_url=json_url,
+                                                   add_new=self._add_new)
+            for filename in filenames:
+                outfilename = os.path.join(subdir, filename);
+                self._RebaselineOneFile(expectations_subdir=subdir,
+                                        builder_name=builder,
+                                        infilename=filename,
+                                        outfilename=outfilename,
+                                        all_results=all_results)
 
 # main...
 
@@ -385,6 +392,11 @@
                     help='instead of actually downloading files or adding ' +
                     'files to checkout, display a list of operations that ' +
                     'we would normally perform')
+parser.add_argument('--expectations-root',
+                    help='root of expectations directory to update-- should ' +
+                    'contain one or more base-* subdirectories. Defaults to ' +
+                    '%(default)s',
+                    default='.')
 parser.add_argument('--json-base-url',
                     help='base URL from which to read JSON_FILENAME ' +
                     'files; defaults to %(default)s',
@@ -403,9 +415,46 @@
                     'failing tests (according to the actual-results.json ' +
                     'file) will be rebaselined.')
 args = parser.parse_args()
-rebaseliner = Rebaseliner(tests=args.tests, configs=args.configs,
-                          subdirs=args.subdirs, dry_run=args.dry_run,
-                          json_base_url=args.json_base_url,
-                          json_filename=args.json_filename,
-                          add_new=args.add_new)
-rebaseliner.RebaselineAll()
+if args.subdirs:
+    subdirs = args.subdirs
+    missing_json_is_fatal = True
+else:
+    subdirs = sorted(SUBDIR_MAPPING.keys())
+    missing_json_is_fatal = False
+for subdir in subdirs:
+    if not subdir in SUBDIR_MAPPING.keys():
+        raise Exception(('unrecognized platform subdir "%s"; ' +
+                         'should be one of %s') % (
+                             subdir, SUBDIR_MAPPING.keys()))
+    builder = SUBDIR_MAPPING[subdir]
+
+    # We instantiate different Rebaseliner objects depending
+    # on whether we are rebaselining an expected-results.json file, or
+    # individual image files.  Different gm-expected subdirectories may move
+    # from individual image files to JSON-format expectations at different
+    # times, so we need to make this determination per subdirectory.
+    #
+    # See https://goto.google.com/ChecksumTransitionDetail
+    expectations_json_file = os.path.join(args.expectations_root, subdir,
+                                          JSON_EXPECTATIONS_FILENAME)
+    if os.path.isfile(expectations_json_file):
+        sys.stderr.write('ERROR: JsonRebaseliner is not implemented yet.\n')
+        sys.exit(1)
+        rebaseliner = JsonRebaseliner(
+            expectations_root=args.expectations_root,
+            tests=args.tests, configs=args.configs,
+            dry_run=args.dry_run,
+            json_base_url=args.json_base_url,
+            json_filename=args.json_filename,
+            add_new=args.add_new,
+            missing_json_is_fatal=missing_json_is_fatal)
+    else:
+        rebaseliner = rebaseline_imagefiles.ImageRebaseliner(
+            expectations_root=args.expectations_root,
+            tests=args.tests, configs=args.configs,
+            dry_run=args.dry_run,
+            json_base_url=args.json_base_url,
+            json_filename=args.json_filename,
+            add_new=args.add_new,
+            missing_json_is_fatal=missing_json_is_fatal)
+    rebaseliner.RebaselineSubdir(subdir=subdir, builder=builder)