| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 ''' | 3 ''' |
| 4 Copyright 2013 Google Inc. | 4 Copyright 2013 Google Inc. |
| 5 | 5 |
| 6 Use of this source code is governed by a BSD-style license that can be | 6 Use of this source code is governed by a BSD-style license that can be |
| 7 found in the LICENSE file. | 7 found in the LICENSE file. |
| 8 ''' | 8 ''' |
| 9 | 9 |
| 10 ''' | 10 ''' |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 48 | 48 |
| 49 class ImageRebaseliner(object): | 49 class ImageRebaseliner(object): |
| 50 | 50 |
| 51 # params: | 51 # params: |
| 52 # expectations_root: root directory of all expectations | 52 # expectations_root: root directory of all expectations |
| 53 # json_base_url: base URL from which to read json_filename | 53 # json_base_url: base URL from which to read json_filename |
| 54 # json_filename: filename (under json_base_url) from which to read a | 54 # json_filename: filename (under json_base_url) from which to read a |
| 55 # summary of results; typically "actual-results.json" | 55 # summary of results; typically "actual-results.json" |
| 56 # tests: list of tests to rebaseline, or None if we should rebaseline | 56 # tests: list of tests to rebaseline, or None if we should rebaseline |
| 57 # whatever files the JSON results summary file tells us to | 57 # whatever files the JSON results summary file tells us to |
| 58 # configs: which configs to run for each test; this should only be | 58 # configs: which configs to run for each test, or None if we should |
| 59 # specified if the list of tests was also specified (otherwise, | 59 # rebaseline whatever configs the JSON results summary file tells |
| 60 # the JSON file will give us test names and configs) | 60 # us to |
| 61 # dry_run: if True, instead of actually downloading files or adding | 61 # dry_run: if True, instead of actually downloading files or adding |
| 62 # files to checkout, display a list of operations that | 62 # files to checkout, display a list of operations that |
| 63 # we would normally perform | 63 # we would normally perform |
| 64 # add_new: if True, add expectations for tests which don't have any yet | 64 # add_new: if True, add expectations for tests which don't have any yet |
| 65 # missing_json_is_fatal: whether to halt execution if we cannot read a | 65 # missing_json_is_fatal: whether to halt execution if we cannot read a |
| 66 # JSON actual result summary file | 66 # JSON actual result summary file |
| 67 def __init__(self, expectations_root, json_base_url, json_filename, | 67 def __init__(self, expectations_root, json_base_url, json_filename, |
| 68 tests=None, configs=None, dry_run=False, | 68 tests=None, configs=None, dry_run=False, |
| 69 add_new=False, missing_json_is_fatal=False): | 69 add_new=False, missing_json_is_fatal=False): |
| 70 if configs and not tests: | |
| 71 raise ValueError('configs should only be specified if tests ' + | |
| 72 'were specified also') | |
| 73 self._expectations_root = expectations_root | 70 self._expectations_root = expectations_root |
| 74 self._tests = tests | 71 self._tests = tests |
| 75 self._configs = configs | 72 self._configs = configs |
| 76 self._json_base_url = json_base_url | 73 self._json_base_url = json_base_url |
| 77 self._json_filename = json_filename | 74 self._json_filename = json_filename |
| 78 self._dry_run = dry_run | 75 self._dry_run = dry_run |
| 79 self._add_new = add_new | 76 self._add_new = add_new |
| 80 self._missing_json_is_fatal = missing_json_is_fatal | 77 self._missing_json_is_fatal = missing_json_is_fatal |
| 81 self._googlestorage_gm_actuals_root = ( | 78 self._googlestorage_gm_actuals_root = ( |
| 82 'http://chromium-skia-gm.commondatastorage.googleapis.com/gm') | 79 'http://chromium-skia-gm.commondatastorage.googleapis.com/gm') |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 return False | 115 return False |
| 119 url = '%s/%s/%s/%s.png' % (self._googlestorage_gm_actuals_root, | 116 url = '%s/%s/%s/%s.png' % (self._googlestorage_gm_actuals_root, |
| 120 hash_type, test_name, hash_value) | 117 hash_type, test_name, hash_value) |
| 121 try: | 118 try: |
| 122 self._DownloadFile(source_url=url, dest_filename=outfilename) | 119 self._DownloadFile(source_url=url, dest_filename=outfilename) |
| 123 return True | 120 return True |
| 124 except CommandFailedException: | 121 except CommandFailedException: |
| 125 print '# Couldn\'t fetch gs_url %s' % url | 122 print '# Couldn\'t fetch gs_url %s' % url |
| 126 return False | 123 return False |
| 127 | 124 |
| 128 # Download a single actual result from skia-autogen, returning True if it | |
| 129 # succeeded. | |
| 130 def _DownloadFromAutogen(self, infilename, outfilename, | |
| 131 expectations_subdir, builder_name): | |
| 132 url = ('http://skia-autogen.googlecode.com/svn/gm-actual/' + | |
| 133 expectations_subdir + '/' + builder_name + '/' + | |
| 134 expectations_subdir + '/' + infilename) | |
| 135 try: | |
| 136 self._DownloadFile(source_url=url, dest_filename=outfilename) | |
| 137 return True | |
| 138 except CommandFailedException: | |
| 139 print '# Couldn\'t fetch autogen_url %s' % url | |
| 140 return False | |
| 141 | |
| 142 # Download a single file, raising a CommandFailedException if it fails. | 125 # Download a single file, raising a CommandFailedException if it fails. |
| 143 def _DownloadFile(self, source_url, dest_filename): | 126 def _DownloadFile(self, source_url, dest_filename): |
| 144 # Download into a temporary file and then rename it afterwards, | 127 # Download into a temporary file and then rename it afterwards, |
| 145 # so that we don't corrupt the existing file if it fails midway thru. | 128 # so that we don't corrupt the existing file if it fails midway thru. |
| 146 temp_filename = os.path.join(os.path.dirname(dest_filename), | 129 temp_filename = os.path.join(os.path.dirname(dest_filename), |
| 147 '.temp-' + os.path.basename(dest_filename)) | 130 '.temp-' + os.path.basename(dest_filename)) |
| 148 | 131 |
| 149 # TODO(epoger): Replace calls to "curl"/"mv" (which will only work on | 132 # TODO(epoger): Replace calls to "curl"/"mv" (which will only work on |
| 150 # Unix) with a Python HTTP library (which should work cross-platform) | 133 # Unix) with a Python HTTP library (which should work cross-platform) |
| 151 self._Call([ 'curl', '--fail', '--silent', source_url, | 134 self._Call([ 'curl', '--fail', '--silent', source_url, |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 240 print '#' | 223 print '#' |
| 241 return files_to_rebaseline | 224 return files_to_rebaseline |
| 242 | 225 |
| 243 # Rebaseline a single file. | 226 # Rebaseline a single file. |
| 244 def _RebaselineOneFile(self, expectations_subdir, builder_name, | 227 def _RebaselineOneFile(self, expectations_subdir, builder_name, |
| 245 infilename, outfilename, all_results): | 228 infilename, outfilename, all_results): |
| 246 if self._dry_run: | 229 if self._dry_run: |
| 247 print '' | 230 print '' |
| 248 print '# ' + infilename | 231 print '# ' + infilename |
| 249 | 232 |
| 250 # First try to download this result image from Google Storage. | 233 # Download this result image from Google Storage; if that fails, |
| 251 # If that fails, try skia-autogen. | 234 # raise an exception (because if actual-results.json told us that |
| 252 # If that fails too, just go on to the next file. | 235 # a particular image version is available for download, we should |
| 253 # | 236 # always be able to get it!) |
| 254 # This not treated as a fatal failure because not all | |
| 255 # platforms generate all configs (e.g., Android does not | |
| 256 # generate PDF). | |
| 257 # | |
| 258 # TODO(epoger): Once we are downloading only files that the | |
| 259 # actual-results.json file told us to, this should become a | |
| 260 # fatal error. (If the actual-results.json file told us that | |
| 261 # the test failed with XXX results, we should be able to download | |
| 262 # those results every time.) | |
| 263 if not self._DownloadFromGoogleStorage(infilename=infilename, | 237 if not self._DownloadFromGoogleStorage(infilename=infilename, |
| 264 outfilename=outfilename, | 238 outfilename=outfilename, |
| 265 all_results=all_results): | 239 all_results=all_results): |
| 266 if not self._DownloadFromAutogen(infilename=infilename, | 240 raise Exception('# Couldn\'t fetch infilename ' + infilename) |
| 267 outfilename=outfilename, | |
| 268 expectations_subdir=expectations_su
bdir, | |
| 269 builder_name=builder_name): | |
| 270 print '# Couldn\'t fetch infilename ' + infilename | |
| 271 return | |
| 272 | 241 |
| 273 # Add this file to version control (if appropriate). | 242 # Add this file to version control (if appropriate). |
| 274 if self._add_new: | 243 if self._add_new: |
| 275 if self._is_svn_checkout: | 244 if self._is_svn_checkout: |
| 276 cmd = [ 'svn', 'add', '--quiet', outfilename ] | 245 cmd = [ 'svn', 'add', '--quiet', outfilename ] |
| 277 self._Call(cmd) | 246 self._Call(cmd) |
| 278 cmd = [ 'svn', 'propset', '--quiet', 'svn:mime-type', | 247 cmd = [ 'svn', 'propset', '--quiet', 'svn:mime-type', |
| 279 'image/png', outfilename ]; | 248 'image/png', outfilename ]; |
| 280 self._Call(cmd) | 249 self._Call(cmd) |
| 281 elif self._is_git_checkout: | 250 elif self._is_git_checkout: |
| 282 cmd = [ 'git', 'add', outfilename ] | 251 cmd = [ 'git', 'add', outfilename ] |
| 283 self._Call(cmd) | 252 self._Call(cmd) |
| 284 | 253 |
| 285 # Rebaseline the given configs for a single test. | |
| 286 # | |
| 287 # params: | |
| 288 # expectations_subdir | |
| 289 # builder_name | |
| 290 # test: a single test to rebaseline | |
| 291 # all_results: a dictionary of all actual results | |
| 292 def _RebaselineOneTest(self, expectations_subdir, builder_name, test, | |
| 293 all_results): | |
| 294 if self._configs: | |
| 295 configs = self._configs | |
| 296 else: | |
| 297 if (expectations_subdir == 'base-shuttle-win7-intel-angle'): | |
| 298 configs = [ 'angle', 'anglemsaa16' ] | |
| 299 else: | |
| 300 configs = [ '565', '8888', 'gpu', 'pdf', 'mesa', 'msaa16', | |
| 301 'msaa4' ] | |
| 302 if self._dry_run: | |
| 303 print '' | |
| 304 print '# ' + expectations_subdir + ':' | |
| 305 for config in configs: | |
| 306 infilename = test + '_' + config + '.png' | |
| 307 outfilename = os.path.join(self._expectations_root, | |
| 308 expectations_subdir, infilename); | |
| 309 self._RebaselineOneFile(expectations_subdir=expectations_subdir, | |
| 310 builder_name=builder_name, | |
| 311 infilename=infilename, | |
| 312 outfilename=outfilename, | |
| 313 all_results=all_results) | |
| 314 | |
| 315 # Rebaseline all tests/types we specified in the constructor, | 254 # Rebaseline all tests/types we specified in the constructor, |
| 316 # within this gm-expectations subdir. | 255 # within this gm-expectations subdir. |
| 317 # | 256 # |
| 318 # params: | 257 # params: |
| 319 # subdir : e.g. 'base-shuttle-win7-intel-float' | 258 # subdir : e.g. 'base-shuttle-win7-intel-float' |
| 320 # builder : e.g. 'Test-Win7-ShuttleA-HD2000-x86-Release' | 259 # builder : e.g. 'Test-Win7-ShuttleA-HD2000-x86-Release' |
| 321 def RebaselineSubdir(self, subdir, builder): | 260 def RebaselineSubdir(self, subdir, builder): |
| 322 json_url = '/'.join([self._json_base_url, | 261 json_url = '/'.join([self._json_base_url, |
| 323 subdir, builder, subdir, | 262 subdir, builder, subdir, |
| 324 self._json_filename]) | 263 self._json_filename]) |
| 325 all_results = self._GetActualResults(json_url=json_url) | 264 all_results = self._GetActualResults(json_url=json_url) |
| 265 filenames = self._GetFilesToRebaseline(json_url=json_url, |
| 266 add_new=self._add_new) |
| 267 skipped_files = [] |
| 268 for filename in filenames: |
| 269 (test, config) = self._testname_pattern.match(filename).groups() |
| 270 if self._tests: |
| 271 if test not in self._tests: |
| 272 skipped_files.append(filename) |
| 273 continue |
| 274 if self._configs: |
| 275 if config not in self._configs: |
| 276 skipped_files.append(filename) |
| 277 continue |
| 278 outfilename = os.path.join(subdir, filename); |
| 279 self._RebaselineOneFile(expectations_subdir=subdir, |
| 280 builder_name=builder, |
| 281 infilename=filename, |
| 282 outfilename=outfilename, |
| 283 all_results=all_results) |
| 326 | 284 |
| 327 if self._tests: | 285 if skipped_files: |
| 328 for test in self._tests: | 286 print ('Skipped these files due to test/config filters: %s' % |
| 329 self._RebaselineOneTest(expectations_subdir=subdir, | 287 skipped_files) |
| 330 builder_name=builder, | |
| 331 test=test, all_results=all_results) | |
| 332 else: # get the raw list of files that need rebaselining from JSON | |
| 333 filenames = self._GetFilesToRebaseline(json_url=json_url, | |
| 334 add_new=self._add_new) | |
| 335 for filename in filenames: | |
| 336 outfilename = os.path.join(subdir, filename); | |
| 337 self._RebaselineOneFile(expectations_subdir=subdir, | |
| 338 builder_name=builder, | |
| 339 infilename=filename, | |
| 340 outfilename=outfilename, | |
| 341 all_results=all_results) | |
| OLD | NEW |