Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(82)

Side by Side Diff: tools/svndiff.py

Issue 19112002: svndiff.py: add ability to compare before-and-after JSON files, not just raw images (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: add_CreateGmActualURL Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 ''' 2 '''
3 Generates a visual diff of all pending changes in the local SVN checkout. 3 Generates a visual diff of all pending changes in the local SVN checkout.
4 4
5 Launch with --help to see more information. 5 Launch with --help to see more information.
6 6
7 7
8 Copyright 2012 Google Inc. 8 Copyright 2012 Google Inc.
9 9
10 Use of this source code is governed by a BSD-style license that can be 10 Use of this source code is governed by a BSD-style license that can be
11 found in the LICENSE file. 11 found in the LICENSE file.
12 ''' 12 '''
13 13
14 # common Python modules 14 # common Python modules
15 import optparse 15 import optparse
16 import os 16 import os
17 import re 17 import re
18 import shutil 18 import shutil
19 import sys
19 import tempfile 20 import tempfile
21 import urllib2
20 22
21 # modules declared within this same directory 23 # Imports from within Skia
24 #
25 # We need to add the 'gm' directory, so that we can import gm_json.py within
26 # that directory. That script allows us to parse the actual-results.json file
27 # written out by the GM tool.
28 # Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end*
29 # so any dirs that are already in the PYTHONPATH will be preferred.
30 #
31 # This assumes that the 'gm' directory has been checked out as a sibling of
32 # the 'tools' directory containing this script, which will be the case if
33 # 'trunk' was checked out as a single unit.
34 GM_DIRECTORY = os.path.realpath(
35 os.path.join(os.path.dirname(os.path.dirname(__file__)), 'gm'))
36 if GM_DIRECTORY not in sys.path:
37 sys.path.append(GM_DIRECTORY)
38 import gm_json
39 import jsondiff
22 import svn 40 import svn
23 41
24 USAGE_STRING = 'Usage: %s [options]' 42 USAGE_STRING = 'Usage: %s [options]'
25 HELP_STRING = ''' 43 HELP_STRING = '''
26 44
27 Generates a visual diff of all pending changes in the local SVN checkout. 45 Generates a visual diff of all pending changes in the local SVN checkout.
28 46
29 This includes a list of all files that have been added, deleted, or modified 47 This includes a list of all files that have been added, deleted, or modified
30 (as far as SVN knows about). For any image modifications, pixel diffs will 48 (as far as SVN knows about). For any image modifications, pixel diffs will
31 be generated. 49 be generated.
32 50
33 ''' 51 '''
34 52
53 IMAGE_FILENAME_RE = re.compile(gm_json.IMAGE_FILENAME_PATTERN)
54
35 TRUNK_PATH = os.path.join(os.path.dirname(__file__), os.pardir) 55 TRUNK_PATH = os.path.join(os.path.dirname(__file__), os.pardir)
36 56
37 OPTION_DEST_DIR = '--dest-dir' 57 OPTION_DEST_DIR = '--dest-dir'
38 # default DEST_DIR is determined at runtime
39 OPTION_PATH_TO_SKDIFF = '--path-to-skdiff' 58 OPTION_PATH_TO_SKDIFF = '--path-to-skdiff'
40 # default PATH_TO_SKDIFF is determined at runtime 59 OPTION_SOURCE_DIR = '--source-dir'
41 60
42 def RunCommand(command): 61 def RunCommand(command):
43 """Run a command, raising an exception if it fails. 62 """Run a command, raising an exception if it fails.
44 63
45 @param command the command as a single string 64 @param command the command as a single string
46 """ 65 """
47 print 'running command [%s]...' % command 66 print 'running command [%s]...' % command
48 retval = os.system(command) 67 retval = os.system(command)
49 if retval is not 0: 68 if retval is not 0:
50 raise Exception('command [%s] failed' % command) 69 raise Exception('command [%s] failed' % command)
(...skipping 13 matching lines...) Expand all
64 trunk_path = os.path.join(os.path.dirname(__file__), os.pardir) 83 trunk_path = os.path.join(os.path.dirname(__file__), os.pardir)
65 possible_paths = [os.path.join(trunk_path, 'out', 'Release', 'skdiff'), 84 possible_paths = [os.path.join(trunk_path, 'out', 'Release', 'skdiff'),
66 os.path.join(trunk_path, 'out', 'Debug', 'skdiff')] 85 os.path.join(trunk_path, 'out', 'Debug', 'skdiff')]
67 for try_path in possible_paths: 86 for try_path in possible_paths:
68 if os.path.isfile(try_path): 87 if os.path.isfile(try_path):
69 return try_path 88 return try_path
70 raise Exception('cannot find skdiff in paths %s; maybe you need to ' 89 raise Exception('cannot find skdiff in paths %s; maybe you need to '
71 'specify the %s option or build skdiff?' % ( 90 'specify the %s option or build skdiff?' % (
72 possible_paths, OPTION_PATH_TO_SKDIFF)) 91 possible_paths, OPTION_PATH_TO_SKDIFF))
73 92
74 def SvnDiff(path_to_skdiff, dest_dir): 93 def _DownloadUrlToFile(source_url, dest_path):
75 """Generates a visual diff of all pending changes in the local SVN checkout. 94 """Download source_url, and save its contents to dest_path.
95 Raises an exception if there were any problems."""
96 reader = urllib2.urlopen(source_url)
97 writer = open(dest_path, 'wb')
98 writer.write(reader.read())
99 writer.close()
100
borenet 2013/07/16 18:31:03 Nit: spacing
101 def _CreateGSUrl(imagename, hash_type, hash_digest):
102 """Return the HTTP URL we can use to download this particular version of
103 the actually-generated GM image with this imagename.
104
105 imagename: name of the test image, e.g. 'perlinnoise_msaa4.png'
106 hash_type: string indicating the hash type used to generate hash_digest,
107 e.g. gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5
108 hash_digest: the hash digest of the image to retrieve
109 """
110 return gm_json.CreateGmActualUrl(
111 test_name=IMAGE_FILENAME_RE.match(imagename).group(1),
112 hash_type=hash_type,
113 hash_digest=hash_digest)
114
borenet 2013/07/16 18:31:03 Spacing
115 def _CallJsonDiff(old_json_path, new_json_path,
116 old_flattened_dir, new_flattened_dir,
117 filename_prefix):
118 """Using jsondiff.py, write the images that differ between two GM
119 expectations summary files (old and new) into old_flattened_dir and
120 new_flattened_dir.
121
122 filename_prefix: prefix to prepend to filenames of all images we write
123 into the flattened directories
124 """
125 json_differ = jsondiff.GMDiffer()
126 diff_dict = json_differ.GenerateDiffDict(oldfile=old_json_path,
127 newfile=new_json_path)
128 for (imagename, results) in diff_dict.iteritems():
129 old_checksum = results['old']
130 new_checksum = results['new']
131 # TODO(epoger): Currently, this assumes that all images have been
132 # checksummed using gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5
133 old_image_url = _CreateGSUrl(
134 imagename=imagename,
135 hash_type=gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5,
136 hash_digest=old_checksum)
137 new_image_url = _CreateGSUrl(
138 imagename=imagename,
139 hash_type=gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5,
140 hash_digest=new_checksum)
141 _DownloadUrlToFile(
142 source_url=old_image_url,
143 dest_path=os.path.join(old_flattened_dir,
144 filename_prefix + imagename))
145 _DownloadUrlToFile(
146 source_url=new_image_url,
147 dest_path=os.path.join(new_flattened_dir,
148 filename_prefix + imagename))
149
borenet 2013/07/16 18:31:03 Spacing
150 def SvnDiff(path_to_skdiff, dest_dir, source_dir):
151 """Generates a visual diff of all pending changes in source_dir.
76 152
77 @param path_to_skdiff 153 @param path_to_skdiff
78 @param dest_dir existing directory within which to write results 154 @param dest_dir existing directory within which to write results
155 @param source_dir
79 """ 156 """
80 # Validate parameters, filling in default values if necessary and possible. 157 # Validate parameters, filling in default values if necessary and possible.
81 path_to_skdiff = FindPathToSkDiff(path_to_skdiff) 158 path_to_skdiff = os.path.abspath(FindPathToSkDiff(path_to_skdiff))
82 if not dest_dir: 159 if not dest_dir:
83 dest_dir = tempfile.mkdtemp() 160 dest_dir = tempfile.mkdtemp()
161 dest_dir = os.path.abspath(dest_dir)
162
163 os.chdir(source_dir)
84 164
85 # Prepare temporary directories. 165 # Prepare temporary directories.
86 modified_flattened_dir = os.path.join(dest_dir, 'modified_flattened') 166 modified_flattened_dir = os.path.join(dest_dir, 'modified_flattened')
87 original_flattened_dir = os.path.join(dest_dir, 'original_flattened') 167 original_flattened_dir = os.path.join(dest_dir, 'original_flattened')
88 diff_dir = os.path.join(dest_dir, 'diffs') 168 diff_dir = os.path.join(dest_dir, 'diffs')
89 for dir in [modified_flattened_dir, original_flattened_dir, diff_dir] : 169 for dir in [modified_flattened_dir, original_flattened_dir, diff_dir] :
90 shutil.rmtree(dir, ignore_errors=True) 170 shutil.rmtree(dir, ignore_errors=True)
91 os.mkdir(dir) 171 os.mkdir(dir)
92 172
93 # Get a list of all locally modified (including added/deleted) files, 173 # Get a list of all locally modified (including added/deleted) files,
94 # descending subdirectories. 174 # descending subdirectories.
95 svn_repo = svn.Svn('.') 175 svn_repo = svn.Svn('.')
96 modified_file_paths = svn_repo.GetFilesWithStatus( 176 modified_file_paths = svn_repo.GetFilesWithStatus(
97 svn.STATUS_ADDED | svn.STATUS_DELETED | svn.STATUS_MODIFIED) 177 svn.STATUS_ADDED | svn.STATUS_DELETED | svn.STATUS_MODIFIED)
98 178
99 # For each modified file: 179 # For each modified file:
100 # 1. copy its current contents into modified_flattened_dir 180 # 1. copy its current contents into modified_flattened_dir
101 # 2. copy its original contents into original_flattened_dir 181 # 2. copy its original contents into original_flattened_dir
102 for modified_file_path in modified_file_paths: 182 for modified_file_path in modified_file_paths:
103 dest_filename = re.sub(os.sep, '__', modified_file_path) 183 if modified_file_path.endswith('.json'):
104 # If the file had STATUS_DELETED, it won't exist anymore... 184 # Special handling for JSON files, in the hopes that they
105 if os.path.isfile(modified_file_path): 185 # contain GM result summaries.
106 shutil.copyfile(modified_file_path, 186 (_unused, original_file_path) = tempfile.mkstemp()
107 os.path.join(modified_flattened_dir, dest_filename)) 187 svn_repo.ExportBaseVersionOfFile(modified_file_path,
108 svn_repo.ExportBaseVersionOfFile( 188 original_file_path)
109 modified_file_path, 189 platform_prefix = re.sub(os.sep, '__',
110 os.path.join(original_flattened_dir, dest_filename)) 190 os.path.dirname(modified_file_path)) + '__'
191 _CallJsonDiff(old_json_path=original_file_path,
192 new_json_path=modified_file_path,
193 old_flattened_dir=original_flattened_dir,
194 new_flattened_dir=modified_flattened_dir,
195 filename_prefix=platform_prefix)
196 os.remove(original_file_path)
197 else:
198 dest_filename = re.sub(os.sep, '__', modified_file_path)
199 # If the file had STATUS_DELETED, it won't exist anymore...
200 if os.path.isfile(modified_file_path):
201 shutil.copyfile(modified_file_path,
202 os.path.join(modified_flattened_dir, dest_filena me))
203 svn_repo.ExportBaseVersionOfFile(
204 modified_file_path,
205 os.path.join(original_flattened_dir, dest_filename))
111 206
112 # Run skdiff: compare original_flattened_dir against modified_flattened_dir 207 # Run skdiff: compare original_flattened_dir against modified_flattened_dir
113 RunCommand('%s %s %s %s' % (path_to_skdiff, original_flattened_dir, 208 RunCommand('%s %s %s %s' % (path_to_skdiff, original_flattened_dir,
114 modified_flattened_dir, diff_dir)) 209 modified_flattened_dir, diff_dir))
115 print '\nskdiff results are ready in file://%s/index.html' % diff_dir 210 print '\nskdiff results are ready in file://%s/index.html' % diff_dir
116 211
117 def RaiseUsageException(): 212 def RaiseUsageException():
118 raise Exception('%s\nRun with --help for more detail.' % ( 213 raise Exception('%s\nRun with --help for more detail.' % (
119 USAGE_STRING % __file__)) 214 USAGE_STRING % __file__))
120 215
121 def Main(options, args): 216 def Main(options, args):
122 """Allow other scripts to call this script with fake command-line args. 217 """Allow other scripts to call this script with fake command-line args.
123 """ 218 """
124 num_args = len(args) 219 num_args = len(args)
125 if num_args != 0: 220 if num_args != 0:
126 RaiseUsageException() 221 RaiseUsageException()
127 SvnDiff(path_to_skdiff=options.path_to_skdiff, dest_dir=options.dest_dir) 222 SvnDiff(path_to_skdiff=options.path_to_skdiff, dest_dir=options.dest_dir,
223 source_dir=options.source_dir)
128 224
129 if __name__ == '__main__': 225 if __name__ == '__main__':
130 parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING) 226 parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING)
131 parser.add_option(OPTION_DEST_DIR, 227 parser.add_option(OPTION_DEST_DIR,
132 action='store', type='string', default=None, 228 action='store', type='string', default=None,
133 help='existing directory within which to write results; ' 229 help='existing directory within which to write results; '
134 'if not set, will create a temporary directory which ' 230 'if not set, will create a temporary directory which '
135 'will remain in place after this script completes') 231 'will remain in place after this script completes')
136 parser.add_option(OPTION_PATH_TO_SKDIFF, 232 parser.add_option(OPTION_PATH_TO_SKDIFF,
137 action='store', type='string', default=None, 233 action='store', type='string', default=None,
138 help='path to already-built skdiff tool; if not set, ' 234 help='path to already-built skdiff tool; if not set, '
139 'will search for it in typical directories near this ' 235 'will search for it in typical directories near this '
140 'script') 236 'script')
237 parser.add_option(OPTION_SOURCE_DIR,
238 action='store', type='string', default='.',
239 help='root directory within which to compare all ' +
240 'files; defaults to "%default"')
141 (options, args) = parser.parse_args() 241 (options, args) = parser.parse_args()
142 Main(options, args) 242 Main(options, args)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698