Chromium Code Reviews| Index: tools/svndiff.py |
| =================================================================== |
| --- tools/svndiff.py (revision 10016) |
| +++ tools/svndiff.py (working copy) |
| @@ -16,9 +16,27 @@ |
| import os |
| import re |
| import shutil |
| +import sys |
| import tempfile |
| +import urllib2 |
| -# modules declared within this same directory |
| +# Imports from within Skia |
| +# |
| +# We need to add the 'gm' directory, so that we can import gm_json.py within |
| +# that directory. That script allows us to parse the actual-results.json file |
| +# written out by the GM tool. |
| +# Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end* |
| +# so any dirs that are already in the PYTHONPATH will be preferred. |
| +# |
| +# This assumes that the 'gm' directory has been checked out as a sibling of |
| +# the 'tools' directory containing this script, which will be the case if |
| +# 'trunk' was checked out as a single unit. |
| +GM_DIRECTORY = os.path.realpath( |
| + os.path.join(os.path.dirname(os.path.dirname(__file__)), 'gm')) |
| +if GM_DIRECTORY not in sys.path: |
| + sys.path.append(GM_DIRECTORY) |
| +import gm_json |
| +import jsondiff |
| import svn |
| USAGE_STRING = 'Usage: %s [options]' |
| @@ -32,12 +50,15 @@ |
| ''' |
| +GOOGLESTORAGE_GM_ACTUALS_ROOT = ( |
| + 'http://chromium-skia-gm.commondatastorage.googleapis.com/gm') |
| +TESTNAME_PATTERN = re.compile('(\S+)_(\S+).png') |
|
borenet
2013/07/12 19:48:04
This is now listed in three places. Not sure how
epoger
2013/07/16 17:29:55
Not so hot, thanks for calling it to my attention.
|
| + |
| TRUNK_PATH = os.path.join(os.path.dirname(__file__), os.pardir) |
| OPTION_DEST_DIR = '--dest-dir' |
| -# default DEST_DIR is determined at runtime |
| OPTION_PATH_TO_SKDIFF = '--path-to-skdiff' |
| -# default PATH_TO_SKDIFF is determined at runtime |
| +OPTION_SOURCE_DIR = '--source-dir' |
| def RunCommand(command): |
| """Run a command, raising an exception if it fails. |
| @@ -71,17 +92,77 @@ |
| 'specify the %s option or build skdiff?' % ( |
| possible_paths, OPTION_PATH_TO_SKDIFF)) |
| -def SvnDiff(path_to_skdiff, dest_dir): |
| - """Generates a visual diff of all pending changes in the local SVN checkout. |
| +def _DownloadUrlToFile(source_url, dest_path): |
| + """Download source_url, and save its contents to dest_path. |
| + Raises an exception if there were any problems.""" |
| + reader = urllib2.urlopen(source_url) |
| + writer = open(dest_path, 'w') |
|
borenet
2013/07/12 19:48:04
You didn't need to open in binary mode for this to
epoger
2013/07/16 17:29:55
Good catch! I was testing it on Linux, where it d
|
| + writer.write(reader.read()) |
| + writer.close() |
| +def _CreateGSUrl(imagename, hash_type, hash_digest): |
| + """Return the HTTP URL we can use to download this particular version of |
| + the actually-generated GM image with this imagename. |
| + |
| + imagename: name of the test image, e.g. 'perlinnoise_msaa4.png' |
| + hash_type: string indicating the hash type used to generate hash_digest, |
| + e.g. gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5 |
| + hash_digest: the hash digest of the image to retrieve |
| + """ |
|
borenet
2013/07/12 19:48:04
Can you add a TODO to consolidate these things?
epoger
2013/07/16 17:29:55
Consolidate which things? Do you mean TESTNAME_PA
borenet
2013/07/16 17:42:13
I was thinking maybe a BuildGMActualGSURLFromImage
epoger
2013/07/16 18:22:48
Sounds good... I created a new gm_json.CreateGmAct
|
| + test_name = TESTNAME_PATTERN.match(imagename).group(1) |
| + return '%s/%s/%s/%s.png' % (GOOGLESTORAGE_GM_ACTUALS_ROOT, |
| + hash_type, test_name, hash_digest) |
| + |
| +def _CallJsonDiff(old_json_path, new_json_path, |
| + old_flattened_dir, new_flattened_dir, |
| + filename_prefix): |
| + """Using jsondiff.py, write the images that differ between two GM |
| + expectations summary files (old and new) into old_flattened_dir and |
| + new_flattened_dir. |
| + |
| + filename_prefix: prefix to prepend to filenames of all images we write |
| + into the flattened directories |
| + """ |
| + json_differ = jsondiff.GMDiffer() |
| + diff_dict = json_differ.GenerateDiffDict(oldfile=old_json_path, |
| + newfile=new_json_path) |
| + for (imagename, results) in diff_dict.iteritems(): |
| + old_checksum = results['old'] |
| + new_checksum = results['new'] |
| + # TODO(epoger): Currently, this assumes that all images have been |
| + # checksummed using gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5 |
| + old_image_url = _CreateGSUrl( |
| + imagename=imagename, |
| + hash_type=gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5, |
| + hash_digest=old_checksum) |
| + new_image_url = _CreateGSUrl( |
| + imagename=imagename, |
| + hash_type=gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5, |
| + hash_digest=new_checksum) |
| + _DownloadUrlToFile( |
| + source_url=old_image_url, |
| + dest_path=os.path.join(old_flattened_dir, |
| + filename_prefix + imagename)) |
| + _DownloadUrlToFile( |
| + source_url=new_image_url, |
| + dest_path=os.path.join(new_flattened_dir, |
| + filename_prefix + imagename)) |
| + |
| +def SvnDiff(path_to_skdiff, dest_dir, source_dir): |
| + """Generates a visual diff of all pending changes in source_dir. |
| + |
| @param path_to_skdiff |
| @param dest_dir existing directory within which to write results |
| + @param source_dir |
| """ |
| # Validate parameters, filling in default values if necessary and possible. |
| - path_to_skdiff = FindPathToSkDiff(path_to_skdiff) |
| + path_to_skdiff = os.path.abspath(FindPathToSkDiff(path_to_skdiff)) |
| if not dest_dir: |
| dest_dir = tempfile.mkdtemp() |
| + dest_dir = os.path.abspath(dest_dir) |
| + os.chdir(source_dir) |
| + |
| # Prepare temporary directories. |
| modified_flattened_dir = os.path.join(dest_dir, 'modified_flattened') |
| original_flattened_dir = os.path.join(dest_dir, 'original_flattened') |
| @@ -100,14 +181,29 @@ |
| # 1. copy its current contents into modified_flattened_dir |
| # 2. copy its original contents into original_flattened_dir |
| for modified_file_path in modified_file_paths: |
| - dest_filename = re.sub(os.sep, '__', modified_file_path) |
| - # If the file had STATUS_DELETED, it won't exist anymore... |
| - if os.path.isfile(modified_file_path): |
| - shutil.copyfile(modified_file_path, |
| - os.path.join(modified_flattened_dir, dest_filename)) |
| - svn_repo.ExportBaseVersionOfFile( |
| - modified_file_path, |
| - os.path.join(original_flattened_dir, dest_filename)) |
| + if modified_file_path.endswith('.json'): |
| + # Special handling for JSON files, in the hopes that they |
| + # contain GM result summaries. |
| + (_unused, original_file_path) = tempfile.mkstemp() |
| + svn_repo.ExportBaseVersionOfFile(modified_file_path, |
| + original_file_path) |
| + platform_prefix = re.sub(os.sep, '__', |
| + os.path.dirname(modified_file_path)) + '__' |
| + _CallJsonDiff(old_json_path=original_file_path, |
| + new_json_path=modified_file_path, |
| + old_flattened_dir=original_flattened_dir, |
| + new_flattened_dir=modified_flattened_dir, |
| + filename_prefix=platform_prefix) |
| + os.remove(original_file_path) |
| + else: |
| + dest_filename = re.sub(os.sep, '__', modified_file_path) |
| + # If the file had STATUS_DELETED, it won't exist anymore... |
| + if os.path.isfile(modified_file_path): |
| + shutil.copyfile(modified_file_path, |
| + os.path.join(modified_flattened_dir, dest_filename)) |
| + svn_repo.ExportBaseVersionOfFile( |
| + modified_file_path, |
| + os.path.join(original_flattened_dir, dest_filename)) |
| # Run skdiff: compare original_flattened_dir against modified_flattened_dir |
| RunCommand('%s %s %s %s' % (path_to_skdiff, original_flattened_dir, |
| @@ -124,7 +220,8 @@ |
| num_args = len(args) |
| if num_args != 0: |
| RaiseUsageException() |
| - SvnDiff(path_to_skdiff=options.path_to_skdiff, dest_dir=options.dest_dir) |
| + SvnDiff(path_to_skdiff=options.path_to_skdiff, dest_dir=options.dest_dir, |
| + source_dir=options.source_dir) |
| if __name__ == '__main__': |
| parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING) |
| @@ -138,5 +235,9 @@ |
| help='path to already-built skdiff tool; if not set, ' |
| 'will search for it in typical directories near this ' |
| 'script') |
| + parser.add_option(OPTION_SOURCE_DIR, |
| + action='store', type='string', default='.', |
| + help='root directory within which to compare all ' + |
| + 'files; defaults to "%default"') |
| (options, args) = parser.parse_args() |
| Main(options, args) |