| ''' |
| Compares the gm results within the local checkout against those already |
| committed to the Skia repository. |
| |
| Launch with --help to see more information. |
| |
| |
| Copyright 2012 Google Inc. |
| |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| ''' |
| |
| # common Python modules |
| import fnmatch |
| import optparse |
| import os |
| import shutil |
| import tempfile |
| |
| # modules declared within this same directory |
| import svn |
| |
| USAGE_STRING = 'Usage: %s [options]' |
| HOWTO_STRING = ''' |
| To update the checked-in baselines across all platforms, follow these steps: |
| |
| cd .../trunk |
| svn update |
| svn stat # and make sure there are no files awaiting svn commit |
| make tools BUILDTYPE=Release |
| python tools/download_baselines.py |
| python tools/compare_baselines.py |
| # view compare_baselines output in a browser and make sure it's reasonable |
| # upload CL for review |
| # validate that the diffs look right in the review tool |
| # commit CL |
| |
| Note that the above instructions will only *update* already-checked-in |
| baseline images; if you want to check in new baseline images (ones that the |
| bots have been generating but we don't have a golden master for yet), you need |
| to use download_baselines.py's --add-new-files option. |
| ''' |
| HELP_STRING = ''' |
| |
| Compares the gm results within the local checkout against those already |
| committed to the Skia repository. Relies on skdiff to do the low-level |
| comparison. |
| |
| ''' + HOWTO_STRING |
| |
| TRUNK_PATH = os.path.join(os.path.dirname(__file__), os.pardir) |
| |
| OPTION_GM_BASEDIR = '--gm-basedir' |
| DEFAULT_GM_BASEDIR = os.path.join(TRUNK_PATH, 'gm') |
| OPTION_PATH_TO_SKDIFF = '--path-to-skdiff' |
| # default PATH_TO_SKDIFF is determined at runtime |
| OPTION_SVN_GM_URL = '--svn-gm-url' |
| DEFAULT_SVN_GM_URL = 'http://skia.googlecode.com/svn/trunk/gm' |
| |
| def CopyAllFilesAddingPrefix(source_dir, dest_dir, prefix): |
| """Copy all files from source_dir into dest_dir, adding prefix to the name |
| of each one as we copy it. |
| prefixes. |
| |
| @param source_dir |
| @param dest_dir where to save the copied files |
| @param prefix prefix to add to each filename when we make the copy |
| """ |
| all_filenames = os.listdir(source_dir) |
| for filename in all_filenames: |
| source_path = os.path.join(source_dir, filename) |
| if os.path.isdir(source_path): |
| print 'skipping %s because it is a directory, not a file' % filename |
| continue |
| dest_path = os.path.join(dest_dir, '%s%s' % (prefix, filename)) |
| shutil.copyfile(source_path, dest_path) |
| |
| def Flatten(source_dir, dest_dir, subdirectory_pattern): |
| """Copy all files from matching subdirectories under source_dir into |
| dest_dir, flattened into a single directory using subdirectory names as |
| prefixes. |
| |
| @param source_dir |
| @param dest_dir where to save the copied files |
| @param subdirectory_pattern only copy files from subdirectories that match |
| this Unix-style filename pattern (e.g., 'base-*') |
| """ |
| all_filenames = os.listdir(source_dir) |
| matching_filenames = fnmatch.filter(all_filenames, subdirectory_pattern) |
| for filename in matching_filenames: |
| source_path = os.path.join(source_dir, filename) |
| if not os.path.isdir(source_path): |
| print 'skipping %s because it is a file, not a directory' % filename |
| continue |
| print 'flattening directory %s' % source_path |
| CopyAllFilesAddingPrefix(source_dir=source_path, dest_dir=dest_dir, |
| prefix='%s_' % filename) |
| |
| def RunCommand(command): |
| """Run a command, raising an exception if it fails. |
| |
| @param command the command as a single string |
| """ |
| print 'running command [%s]...' % command |
| retval = os.system(command) |
| if retval is not 0: |
| raise Exception('command [%s] failed' % command) |
| |
| def FindPathToSkDiff(user_set_path=None): |
| """Return path to an existing skdiff binary, or raise an exception if we |
| cannot find one. |
| |
| @param user_set_path if None, the user did not specify a path, so look in |
| some likely places; otherwise, only check at this path |
| """ |
| if user_set_path is not None: |
| if os.path.isfile(user_set_path): |
| return user_set_path |
| raise Exception('unable to find skdiff at user-set path %s' % |
| user_set_path) |
| trunk_path = os.path.join(os.path.dirname(__file__), os.pardir) |
| possible_paths = [os.path.join(trunk_path, 'out', 'Release', 'skdiff'), |
| os.path.join(trunk_path, 'out', 'Debug', 'skdiff')] |
| for try_path in possible_paths: |
| if os.path.isfile(try_path): |
| return try_path |
| raise Exception('cannot find skdiff in paths %s; maybe you need to ' |
| 'specify the %s option or build skdiff?' % ( |
| possible_paths, OPTION_PATH_TO_SKDIFF)) |
| |
| def CompareBaselines(gm_basedir, path_to_skdiff, svn_gm_url): |
| """Compare the gm results within gm_basedir against those already |
| committed to the Skia repository. |
| |
| @param gm_basedir |
| @param path_to_skdiff |
| @param svn_gm_url base URL of Subversion repository where we store the |
| expected GM results |
| """ |
| # Validate parameters, filling in default values if necessary and possible. |
| if not os.path.isdir(gm_basedir): |
| raise Exception('cannot find gm_basedir at %s; maybe you need to ' |
| 'specify the %s option?' % ( |
| gm_basedir, OPTION_GM_BASEDIR)) |
| path_to_skdiff = FindPathToSkDiff(path_to_skdiff) |
| |
| tempdir_base = tempfile.mkdtemp() |
| |
| # Download all checked-in baseline images to a temp directory |
| checkedin_dir = os.path.join(tempdir_base, 'checkedin') |
| os.mkdir(checkedin_dir) |
| svn.Svn(checkedin_dir).Checkout(svn_gm_url, '.') |
| |
| # Flatten those checked-in baseline images into checkedin_flattened_dir |
| checkedin_flattened_dir = os.path.join(tempdir_base, 'checkedin_flattened') |
| os.mkdir(checkedin_flattened_dir) |
| Flatten(source_dir=checkedin_dir, dest_dir=checkedin_flattened_dir, |
| subdirectory_pattern='base-*') |
| |
| # Flatten the local baseline images into local_flattened_dir |
| local_flattened_dir = os.path.join(tempdir_base, 'local_flattened') |
| os.mkdir(local_flattened_dir) |
| Flatten(source_dir=gm_basedir, dest_dir=local_flattened_dir, |
| subdirectory_pattern='base-*') |
| |
| # Run skdiff to compare checkedin_flattened_dir against local_flattened_dir |
| diff_dir = os.path.join(tempdir_base, 'diffs') |
| os.mkdir(diff_dir) |
| RunCommand('%s %s %s %s' % (path_to_skdiff, checkedin_flattened_dir, |
| local_flattened_dir, diff_dir)) |
| print '\nskdiff results are ready in file://%s/index.html' % diff_dir |
| # TODO(epoger): delete tempdir_base tree to clean up after ourselves (but |
| # not before the user gets a chance to examine the results), and/or |
| # allow user to specify a different directory to write into? |
| |
| def RaiseUsageException(): |
| raise Exception('%s\nRun with --help for more detail.' % ( |
| USAGE_STRING % __file__)) |
| |
| def Main(options, args): |
| """Allow other scripts to call this script with fake command-line args. |
| """ |
| num_args = len(args) |
| if num_args != 0: |
| RaiseUsageException() |
| CompareBaselines(gm_basedir=options.gm_basedir, |
| path_to_skdiff=options.path_to_skdiff, |
| svn_gm_url=options.svn_gm_url) |
| |
| if __name__ == '__main__': |
| parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING) |
| parser.add_option(OPTION_GM_BASEDIR, |
| action='store', type='string', default=DEFAULT_GM_BASEDIR, |
| help='path to root of locally stored baseline images ' |
| 'to compare against those checked into the svn repo; ' |
| 'defaults to "%s"' % DEFAULT_GM_BASEDIR) |
| parser.add_option(OPTION_PATH_TO_SKDIFF, |
| action='store', type='string', default=None, |
| help='path to already-built skdiff tool; if not set, ' |
| 'will search for it in typical directories near this ' |
| 'script') |
| parser.add_option(OPTION_SVN_GM_URL, |
| action='store', type='string', default=DEFAULT_SVN_GM_URL, |
| help='URL of SVN repository within which we store the ' |
| 'expected GM baseline images; defaults to "%s"' % |
| DEFAULT_SVN_GM_URL) |
| (options, args) = parser.parse_args() |
| Main(options, args) |